react基础04-redux、react-redux、纯函数和高阶函数、serve
antd基本使用
1、安装:npm i antd
2、引入和使用
import React, { Component } from 'react'
import { Button, DatePicker } from 'antd'
import {
WechatOutlined,
WeiboOutlined,
SearchOutlined
} from '@ant-design/icons'
const { RangePicker } = DatePicker
export default class App extends Component {
render() {
return (
<div>
App
<button>点我</button>
<Button type="primary">按钮1</Button>
<Button>按钮2</Button>
<Button type="link">按钮3</Button>
<Button type="primary" icon={<SearchOutlined />}>
Search
</Button>
<WechatOutlined />
<WeiboOutlined />
<DatePicker />
<RangePicker />
</div>
)
}
}
3、引入样式表
import 'antd/dist/antd.css'
antd样式的按需引入
1、安装插件
npm i react-app-rewired customize-cra babel-plugin-import
2、根目录下创建config-overrides.js
const { override, fixBabelImports } = require('customize-cra')
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css'
})
)
3、package.json修改启动命令
"start": "set BROWSER=none&& react-script start", "build": "react-script build", "test": "react-script test",
改为
"start": "set BROWSER=none&& react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test",
4、此时不需要再引入 antd.css 了,直接引入组件即可,重启后,antd组件的js和css都会按需加载
import { Button, DatePicker } from 'antd'
redux
redux是什么
1、redux是一个专门用于状态管理的库,不是react的插件库
2、它可以用在react、vue、angular等项目中,但基本与react配合使用
3、作用:集中式管理react应用中多个组件共享的状态
什么情况下需要使用redux
1、某个组件的状态,需要让其他组件可以随时拿到
2、一个组件需要改变另一个组件的状态(非父子组件通信)
3、总体原则:能不用就不用,组件间传值比较繁琐时考虑使用
redux工作流程

redux的三个核心概念
1、action
同步action,返回一个对象,包含2个属性:type、data 例:{ type: 'add', data: 10 } 这个data可以是任意数据类型
异步action,返回一个函数,函数中第一个参数为dispatch,函数体中执行异步任务
2、reducer
用于初始化状态,加工状态
加工时,根据旧的state和action,产生新的state的纯函数
3、store
将state、action、reducer联系在一起的对象
如何得到store对象?
import { createStore } from 'redux'
const store = createStore(xxxReducer)
如何使用?
store.getState() // 得到state
store.dispatch() // 派发同步或异步action,也可以直接派发reducer
store.subscribe(被监听的) // 注册监听,当产生了新的state时自动调用
redux的核心api
createStore() // 创建包含指定reducer的store对象
applyMiddleware() // 使用基于redux的中间件
combineReducer() // 合并多个reducer函数
redux使用
简洁版:
1、src/redux/store.js
import { createStore } from 'redux'
const countReducer = (prevState = 10, action) => {
const { type, data } = action
switch (type) {
case 'increment':
return prevState + data
case 'decrement':
return prevState - data
default:
return prevState
}
}
export default createStore(countReducer)
2、components/Count/index.jsx
import React, { Component } from 'react'
import store from '../../redux/store'
export default class Count extends Component {
componentDidMount() {
// 监测redux中状态的变化,一旦变化,就调用render
store.subscribe(() => this.setState({})) // 通过this.setState({})触发render函数,传{}表示什么也不改,纯粹为了刷新
}
render() {
return (
<div>
{/* 展示redux中的状态 */}
<h1>当前求和为:{store.getState()}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<br />
<button onClick={() => store.dispatch({ type: 'increment', data: +this.selectNumber.value })} {/*触发redux更新*/} >
+
</button>
<br />
<button onClick={() => store.dispatch({ type: 'decrement', data: +this.selectNumber.value })}>
-
</button>
</div>
)
}
}
3、可以将检测redux的逻辑放在入口文件中:src/index.js
import React from 'react' import ReactDOM from 'react-dom' import App from './App' import store from './redux/store' ReactDOM.render(<App />, document.querySelector('#root')) store.subscribe(() => ReactDOM.render(<App />, document.querySelector('#root')))
只要redux中的状态发生变化,App组件会重新render。有diff算法的加持,效率没问题。好处是不用在每个组件中通过this.setState()去触发当前组件的render函数
完整版:(使用redux推荐的action机制)
1、src/redux/store.js
import { createStore } from 'redux'
const INCREMENT = 'increment',
DECREMENT = 'decrement'
// 通过action对象去触发。实质是将{ type: 'increment', data: +this.selectNumber.value }在store中抽成函数(createIncrementAction),在触发reducer时调用createIncrementAction函数即可
export const createIncrementAction = (data) => ({ type: INCREMENT, data })
export const createDecrementAction = (data) => ({ type: DECREMENT, data })
const countReducer = (prevState = 10, action) => {
const { type, data } = action
switch (type) {
case INCREMENT:
return prevState + data
case DECREMENT:
return prevState - data
default:
return prevState
}
}
export default createStore(countReducer)
2、components/Count/index.jsx
import React, { Component } from 'react'
import store, { createIncrementAction, createDecrementAction } from '../../redux/store'
export default class Count extends Component {
render() {
return (
<div>
{/* 展示redux中的状态 */}
<h1>当前求和为:{store.getState()}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<br />
<button
onClick={() =>
// store.dispatch({type: 'increment',data: +this.selectNumber.value})
store.dispatch(createIncrementAction(+this.selectNumber.value))
} // 触发redux更新
>
+
</button>
<br />
<button
onClick={() =>
// store.dispatch({type: 'decrement',data: +this.selectNumber.value})
store.dispatch(createDecrementAction(+this.selectNumber.value))
}
>
-
</button>
</div>
)
}
}
异步action的使用
1、安装
npm i redux-thunk
2、src/redux/store.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk' // 用于支持异步action
const INCREMENT = 'increment',
DECREMENT = 'decrement'
// 通过action对象去触发。实质是将{ type: 'increment', data: +this.selectNumber.value }在store中抽成函数(createIncrementAction),在触发reducer时调用createIncrementAction函数即可
export const createIncrementAction = (data) => ({ type: INCREMENT, data })
export const createDecrementAction = (data) => ({ type: DECREMENT, data })
// 异步action 函数中一般都会调用同步action
export const createIncrementAsyncAction = (data, time) => (dispatch) =>
setTimeout(() => dispatch(createIncrementAction(data)), time)
const countReducer = (prevState = 10, action) => {
const { type, data } = action
switch (type) {
case INCREMENT:
return prevState + data
case DECREMENT:
return prevState - data
default:
return prevState
}
}
export default createStore(countReducer, applyMiddleware(thunk))
3、components/Count/index.jsx
import React, { Component } from 'react'
import store, { createIncrementAsyncAction } from '../../redux/store'
export default class Count extends Component {
render() {
return (
<div>
{/* 展示redux中的状态 */}
<h1>当前求和为:{store.getState()}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<br />
<button onClick={() => store.dispatch(createIncrementAsyncAction(+this.selectNumber.value, 500))}>
异步加
</button>
</div>
)
}
}
react-redux
react的插件库,简化redux的使用

react-redux将所有组件分为两大类
UI组件:
只负责ui的呈现,不带有任何业务逻辑
通过props接收数据(一般数据和函数)
不使用任何redux的api
一般保存在components文件夹下
容器组件:
负责管理数据和业务逻辑,不负责ui呈现
使用redux的api
一般保存在containers文件夹下
使用:
1、安装:npm i react-redux
2、src/redux/store.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk' // 用于支持异步action
const INCREMENT = 'increment',
DECREMENT = 'decrement'
export const createIncrementAction = (data) => ({ type: INCREMENT, data })
export const createDecrementAction = (data) => ({ type: DECREMENT, data })
// 异步action
export const createIncrementAsyncAction = (data, time) => (dispatch) =>
setTimeout(() => dispatch(createIncrementAction(data)), time)
const countReducer = (prevState = 10, action) => {
const { type, data } = action
switch (type) {
case INCREMENT:
return prevState + data
case DECREMENT:
return prevState - data
default:
return prevState
}
}
export default createStore(countReducer, applyMiddleware(thunk))
3、入口文件:src/index.js
import React from 'react' import ReactDOM from 'react-dom' import App from './App' import store from './redux/store' import { Provider } from 'react-redux' // 通过Provider将store根据需要精准地将store传递给每个组件,这样就不需要在App组件中给每个组件都传递store={store} ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.querySelector('#root') )
4、src/containers/Count/index.jsx【react-redux核心:connect】
import React, { Component } from 'react'
import { connect } from 'react-redux'
import {
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction
} from '../../redux/store'
class Count extends Component {
render() {
const { count, increment, decrement, incrementAsync } = this.props
console.log(this.props)
return (
<div>
<h1>当前求和为:{count}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<br />
<button onClick={() => increment(+this.selectNumber.value)}>+</button>
<br />
<button onClick={() => decrement(+this.selectNumber.value)}>-</button>
<br />
<button onClick={() => incrementAsync(+this.selectNumber.value, 500)}>
异步加
</button>
</div>
)
}
}
/*
connect
1、容器组件通过connect传递给UI组件两个值:
①redux中保存的状态
②用于操作redux状态的方法
2、由react-redux处理,函数第一个函数可以收到state;第二个函数可以收到dispatch。这里不需要再引入store通过store.getState()获取state,也不需要通过store.dispath()去派发,直接用react-redux处理过的state和dispatch就行
3、包裹UI组件,返回容器组件
*/
export default connect(
(state) => ({ count: state }), // 第一个参数:mapStateToProps --> 映射redux中的state变成CountUI组件的props
// 第二个参数:mapDispatchToProps --> 映射redux中的dispatch变成CountUI组件的props,并且不需要subscribe监听触发render函数进行更新,connect会自动触发视图更新
/*
// 一般写法(函数)
(dispatch) => ({
increment: (number) => dispatch(createIncrementAction(number)),
decrement: (number) => dispatch(createDecrementAction(number)),
incrementAsync: (number, time) => dispatch(createIncrementAsyncAction(number, time))
})
*/
// 简写(对象)
{
increment: createIncrementAction, // react-redux会自动触发dispatch
decrement: createDecrementAction,
incrementAsync: createIncrementAsyncAction
}
)(Count)
5、App.jsx
import React, { Component } from 'react'
import Count from './containers/Count'
export default class App extends Component {
render() {
return (
<>
<Count />
</>
)
}
}
多个组件共享redux
1、src/redux/store.js:通过combineReducers汇总reducer(类似vuex中的modules)
import { createStore, applyMiddleware, combineReducers } from 'redux'
import thunk from 'redux-thunk' // 用于支持异步action
const INCREMENT = 'increment',
DECREMENT = 'decrement',
ADD_PERSON = 'add_person'
export const createIncrementAction = (data) => ({ type: INCREMENT, data })
export const createDecrementAction = (data) => ({ type: DECREMENT, data })
// 异步action
export const createIncrementAsyncAction = (data, time) => (dispatch) =>
setTimeout(() => dispatch(createIncrementAction(data)), time)
export const createAddPersonAction = (data) => ({ type: ADD_PERSON, data })
const countReducer = (prevState = 10, action) => {
const { type, data } = action
switch (type) {
case INCREMENT:
return prevState + data
case DECREMENT:
return prevState - data
default:
return prevState
}
}
const initPerson = [{ id: '001', name: '小明', age: 18 }]
const personReducer = (prevState = initPerson, action) => { // 必须是一个纯函数
const { type, data } = action
switch (type) {
case ADD_PERSON:
return [...prevState, data] // prevState.push(data); return prevState 将prevState传入,redux会更新数据,但不会更新视图。因为纯函数中不允许编辑prevState和action,而prevState.push()会更改prevState
default:
return prevState
}
}
const reducers = combineReducers({
count: countReducer,
personList: personReducer
})
export default createStore(reducers, applyMiddleware(thunk))
2、src/index.js
import React from 'react' import ReactDOM from 'react-dom' import App from './App' import store from './redux/store' import { Provider } from 'react-redux' ReactDOM.render( // Provider包裹App组件,让App组件的后代容器组件都能接收到store <Provider store={store}> <App /> </Provider>, document.querySelector('#root') )
3、src/containers/Count/index.jsx
import React, { Component } from 'react'
import { connect } from 'react-redux'
import {
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction
} from '../../redux/store'
class Count extends Component {
render() {
const { count, increment, decrement, incrementAsync, personLength } =
this.props
console.log(this.props)
return (
<div>
<h1>当前求和为:{count}</h1>
<h1>Person组件长度:{personLength}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<br />
<button onClick={() => increment(+this.selectNumber.value)}>+</button>
<br />
<button onClick={() => decrement(+this.selectNumber.value)}>-</button>
<br />
<button onClick={() => incrementAsync(+this.selectNumber.value, 500)}>
异步加
</button>
</div>
)
}
}
export default connect(
(state) => ({ count: state.count, personLength: state.personList.length }),
{
increment: createIncrementAction,
decrement: createDecrementAction,
incrementAsync: createIncrementAsyncAction
}
)(Count)
4、src/containers/Person/index.jsx
import React, { Component } from 'react'
import { nanoid } from 'nanoid' // 需安装nanoid
import { connect } from 'react-redux'
import { createAddPersonAction } from '../../redux/store'
class Person extends Component {
render() {
console.log(this.props)
const { count, personList, createAddPersonAction } = this.props
return (
<div>
<h2>Person组件</h2>
<b>Count组件的值:{count}</b>
<br />
<input ref={(c) => (this.name = c)} type="text" placeholder="名字" />
<input ref={(c) => (this.age = c)} type="text" placeholder="年龄" />
<button
onClick={() => {
const name = this.name.value,
age = this.age.value,
obj = { id: nanoid(), name, age }
createAddPersonAction(obj)
this.name.value = ''
this.age.value = ''
}}
>
添加
</button>
<ul>
{personList.map(({ id, name, age }) => (
<li key={id}>
{name}---{age}
</li>
))}
</ul>
</div>
)
}
}
export default connect(({ personList, count }) => ({ personList, count }), {
createAddPersonAction
})(Person)
5、App.js
import React, { Component } from 'react'
import Count from './containers/Count'
import Person from './containers/Person'
export default class App extends Component {
render() {
return (
<>
<Count />
<hr />
<Person />
</>
)
}
}
6、效果

纯函数和高阶函数
纯函数
1、一种特别的函数,只要是同样的输入(实参),必定得到同样的输出(返回)
2、必须遵守以下一些约束:
①不得改写参数数据
②不会产生任何副作用,例如:网络请求、输入和输出设备
③不能调用Date.now()或Math.rendom()等不纯的方法
3、redux的reducer函数必须是一个纯函数,根据不同的action对象,分别对状态进行不同的处理,这里不能出现任何的DOM操作、ajax请求、计时器等不纯的方法

高阶函数
1、一种特别的函数,参数是函数或者返回值是函数
2、常见的高阶函数:
①定时器中的函数
②数组的forEach、map、filter、reduce、find等方法,bind ……
③promise
④react-redux中的connect函数
3、做用:能实现更加动态,更加可扩展的功能
redux调试工具的使用
1、谷歌浏览器中上传redux开发工具
2、安装插件:npm i redux-devtools-extension
3、store.js
import { composeWithDevTools } from 'redux-devtools-extension'
export default createStore(
reducers,
composeWithDevTools(applyMiddleware(thunk)) // 如果不需要支持异步action就直接传入composeWithDevTools()
)
4、效果

serve
1、npm i serve -g
2、打包当前项目:npm run build
3、执行serve build(将build作为服务器的根路径启动一台服务器)

浙公网安备 33010602011771号