【React自学笔记05】完结撒花~React幼儿园大班毕业啦
七、redux
7.1 redux理解
7.1.1 redux是什么
-
redux是一个专门用于做状态管理的JS库(不是react插件库)。
-
它可以用在react, angular, vue等项目中, 但基本与react配合使用。
-
作用: 集中式管理react应用中多个组件共享的状态。
7.1.2 redux使用场景
-
某个组件的状态,需要让其他组件可以随时拿到(共享)。
-
一个组件需要改变另一个组件的状态(通信)。
-
总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
7.1.3 redux原理图

有点类似于 客人-服务员-店长-大厨 的关系
也类似于 客户-产品-项目经理-程序员 的关系
1. action
-
动作的对象
-
包含2个属性
-
type:标识属性, 值为字符串, 唯一, 必要属性 例:+、-、*、÷
-
data:数据属性, 值类型任意, 可选属性 例:2、3、5、6
-
2. dispatch
-
分发action
-
保证能把action对象继续向下传,交给store
3. Store
-
全局的调度者,指挥者,负责掌控全局
-
将state、action、reducer联系在一起的对象
-
功能
-
getState(): 得到state
-
dispatch(action): 分发action, 触发reducer调用, 产生新的state
-
subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
-
4. reducer
-
用于初始化状态(previousState)、加工状态
-
加工时,根据旧的state(如果没有就是undefined)和action, 产生新的state的纯函数
——————————更新于2022.5.28——————————
7.2 redux使用
version-1
安装 npm install --save redux
创建redux文件夹->新建store.js和XXX_reducer.js
在store.js文件中暴露store对象,整个应用只有一个store对象
-
引入createStore,专门用于创建store对象
-
引入为组件服务的reducer
-
将用createstore()创建的store对象暴露出去
import { legacy_createStore as createStore } from "redux";
import countReducer from './count_reducer'
export default createStroe(countReducer)
在XXX_reducer.js文件中编写
-
该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
-
reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
-
reducer有两个作用:初始化状态、加工状态
-
xxxReducer是一个纯函数,只负责写逻辑 ,不负责判断,接收preState、action,返回加工后的状态
-
reducer被第一次调用时,是store自动触发的
-
传递的preState是undefined
-
传递的action是:
-
export default function countReducer(preState, action) {
// 从action对象中获取:type、data
const { type, data } = action
// 根据type决定如何加工数据
switch (type) {
case 'increment':
return preState + data
case 'decrement':
default:
return 0 //初始化
}
}
使用redux
-
在需要用到状态的组件中引入store
-
获取redux中的count值 store.getState()
-
通知redux加store.dispatch(),将type和data作为参数对象传入
-
redux里面的状态更改不会引起页面更新,redux只负责管理。需要监测一下如果redux中的状态发生改变需要自己调用组件的render
-
在生命周期钩子componentDidMount中调用store.subscribe()函数用于监测redux中状态的变化,只要变化,就调用render【组件只要一挂载,就开始监测】
- componentDidMount()在组件加载完成, render之后进行调用,只会执行一次
-
如果redux中管理的状态很多,每个状态都要在componentDidMount中写一遍store.subscribe(),不如直接把store.subscribe()写到index.js入口文件中,(暂时还不知道怎么写)
import React, { Component } from 'react'
import store from '../../redux/store';
export default class Count extends Component {
componentDidMount() {
store.subscribe(() => {
this.setState({})
})
}
increase = () => {
const value = this.selected.value;
// 通知组件加
store.dispatch({ type: 'increment', data: value * 1 })
}
decrease = () => {
const value = this.selected.value;
store.dispatch({ type: 'decrement', data: value * 1 })
}
increaseIfOdds = () => {
const value = this.selected.value;
const count = store.getState()
if (count % 2 !== 0) {
store.dispatch({ type: 'increment', data: value * 1 })
}
}
increaseIfAsync = () => {
const value = this.selected.value;
setTimeout(() => {
store.dispatch({
type: 'increment', data: value * 1
})
}, 500)
}
render() {
return (
<div>
<h1>当前求和为:{store.getState()}</h1>
<select ref={(cNode) => { this.selected = cNode }}>
<option value="1" >1</option>
<option value="2" >2</option>
<option value="3" >3</option>
</select>
<button onClick={this.increase}>+</button>
<button onClick={this.decrease}>-</button>
<button onClick={this.increaseIfOdds}>奇数再加</button>
<button onClick={this.increaseIfAsync}>异步再加</button>
</div>
)
}
}
——————————更新于2022.5.30——————————
Version-2
在redux文件下新建xxx_action.js文件
-
该文件专门为count组件生成action对象
export const createIncrementAction=(data)=>{ return { type:'increment',data} } export const createDecrementAction=(data)=>{ return { type:'decrement',data} }
在index.jsx文件中
-
引入count_action.js文件,专门用于创建action对象
-
使用创建的action对象作为参数传入 store.dispatch中
import { createIncrementAction, createDecrementAction } from '../../redux/count_action'; increase = () => { const value = this.selected.value; store.dispatch(createIncrementAction(value * 1)) }
在redux文件夹中新建constant.js文件
-
该模块用于定义action对象中type类型的常量值
-
便于管理,为了避免因为type值写错造成的程序无法正常运行问题
-
凡是用到'increment','decrement'的地方都引入constant.js文件并改成INCREMENT和DECREMENT
//constant.js
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
//count_action.js
import { INCREMENT, DECREMENT } from "./constant"
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })
7.3 redux的核心API总结
7.3.1 createstore()
作用:创建包含指定reducer的store对象
7.3.2 store对象
-
作用: redux库最核心的管理对象
-
它内部维护着state和reducer
-
核心方法:
-
getState()
-
dispatch(action)
-
subscribe(listener)
-
-
具体编码:
-
store.getState()
-
store.dispatch({type:'INCREMENT', number})
-
store.subscribe(render)
-
7.3.3 applyMiddleware()
作用:应用上基于redux的中间件(插件库)
7.3.4 combineReducers()
作用:合并多个reducer函数
7.4 redux异步编程
关于异步编程
-
当action为object一般对象称同步action;action的值为function函数称异步action,因为function函数中可以开启异步任务
-
redux默认是不能进行异步处理的
-
某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
异步编程的步骤:
编程创建的action函数不再返回一般对象,而是一个函数,该函数中写异步任务
异步任务有结果后,分法一个同步的action去真正操作数据
//count_action.js
import { INCREMENT, DECREMENT } from "./constant"
import store from "./store"
export const createIncrementAsyncAction=(data,time)=>{
return ()=>{
setTimeout(()=>{
// 通知store开启异步任务
store.dispatch(createIncrementAction(data))
},time)
}
}
//count.js
increaseIfAsync = () => {
const value = this.selected.value;
store.dispatch(createIncrementAsyncAction(value*1,500))
}
使用异步中间件
-
安装中间件npm install --save redux-thunk
-
在store.js中引入thunk import thunk from 'redux-thunk'
//store.js
import { legacy_createStore as createStore ,applyMiddleware} from "redux";
import countReducer from './count_reducer'
import thunk from 'redux-thunk'
export default createStore(countReducer, applyMiddleware(thunk))
💌 注意:异步action中一般都会调用同步action
//count_action.js
import { INCREMENT, DECREMENT } from "./constant"
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })
export const createIncrementAsyncAction = (data, time) => {
return (dispatch) => {
setTimeout(() => {
// 通知store开启异步任务
dispatch(createIncrementAction(data))
}, time)
}
}
🍀总结:异步实现有两种方式
-
类似于客人到餐厅吃饭时,客人自己看菜单看了五分钟,然后点菜(延迟的动作交给组件自身)
-
还有一种客人即刻点菜,但是要求五分钟后再上菜(延迟的动作交给action)
所以:异步action不是必须要用的
7.5 react-redux
7.5.1 理解
-
一个react插件库
-
专门用来简化react应用中使用redux
-
react-redux模型图

react-Redux将所有组件分成两大类
-
UI组件
-
只负责 UI 的呈现,不带有任何业务逻辑
-
通过props接收数据(一般数据和函数)
-
不使用任何 Redux 的 API
-
一般保存在components文件夹下
-
-
容器组件
-
负责管理数据和业务逻辑,不负责UI的呈现
-
使用 Redux 的 API
-
一般保存在containers文件夹下
-
7.5.2 使用
安装 npm install react-redux --save
新建containers文件夹-创建CountUI对应的文件夹,用于UI与容器建立连接
-
引入Count的UI组件
-
引入redux中的store
-
使用connect()()创建并暴露容器组件,用于连接UI组件与redux。connect调用的返回值仍然是一个函数
-
类比容器连接左手的UI,连接右手的redux
import CountUI from '../../components/Count' import store from '../../redux/store' import {connect} from 'react-redux' export default connect()(CountUI)
重新在App.jsx中引入container下的UI组件
-
此时还有一个问题:虽然上述逻辑是正确的,但是容器连接redux不能直接import引入,需要在用到该标签处引入
-
代码更新后如下:
//App.js import React, { Component } from 'react' import Count from './container/Count' import store from './redux/store' export default class App extends Component { render() { return ( <div> <Count store={store}/> </div> ) } }//Count.jsx import CountUI from '../../components/Count' import { connect } from 'react-redux' const CountContainer = connect()(CountUI) export default CountContainer
相关文件代码叙述
🍎Container/Count.js
-
容器组件:负责和redux通信,将结果交给UI组件
-
该文件作文容器左边连接UI组件,右边连接redux,但是右边连接redux时,不直接引用store文件,而是在App.js文件中通过props进行传递,在Count容器中来接收state和dispatch
-
如何创建一个容器组件---靠react-redux的connect函数
connect(mapstateToProps,mapDispatchToProps)(UI组件)
- mapstateToProps:映射状态,返回值是一个对象
- mapDispatchToProps :l映射操作状态的方法,返回值是一个对象
import CountUI from '../../components/Count' import { connect } from 'react-redux' import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' // 函数返回的对象中的key作为 传递给UI组件props的key value就作为传递给UI组件props的value——状态 function mapStateToProps(state) { return { count: state } } // 函数返回的对象中的key作为传递给UI组件props的key value就作为传递给UI组件props的value——操作状态的方法 function mapDispatchToProps(dispatch) { return { add: (data) => { // 通知redux执行加法 dispatch(createIncrementAction(data)) }, minus: (data) => { dispatch(createDecrementAction(data)) }, addAsync(data, time) { dispatch(createIncrementAsyncAction(data, time)) } } } export default connect(mapStateToProps, mapDispatchToProps)(CountUI)
🍊App.js
-
该文件用于接收从redux中传递给Container容器的状态和方法
-
使用标签属性的方式传给容器
import React, { Component } from 'react' import Count from './container/Count' import store from './redux/store' export default class App extends Component { render() { return ( <div> <Count store={store} /> </div> ) } }
🍋Components/Count
-
UI组件,不能使用任何的redux的api,只负责页面的呈现、交互等
-
使用this.props接收状态和方法
import React, { Component } from 'react' export default class Count extends Component { increase = () => { const value = this.selected.value; this.props.add(value * 1) } decrease = () => { const value = this.selected.value; this.props.minus(value * 1) } increaseIfOdds = () => { const value = this.selected.value; if (this.props.count % 2 !== 0) { this.props.add(value * 1) } } increaseIfAsync = () => { const value = this.selected.value; this.props.addAsync(value * 1, 500) } render(){ return {...} } }
————————————记录于2022.5.31————————————
代码优化:
🍐mapDispatchToProps 的简写
import CountUI from '../../components/Count'
import { connect } from 'react-redux'
import {
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction
} from '../../redux/count_action'
// 函数返回的对象中的key作为 传递给UI组件props的key value就作为传递给UI组件props的value——状态
export default connect(
state => ({ count: state }),
{
add: createIncrementAction,
minus: createDecrementAction,
addAsync: createIncrementAsyncAction
}
)(CountUI)
🍇Provider的使用
-
批量给所有容器组件传递store对象
-
不需要用容器组件传store,减少代码冗余
-
在入口文件index.js中引入< Provider>标签
import React from 'react'; import ReactDOM from 'react-dom/client'; import store from './redux/store'; import App from './App'; import { Provider } from 'react-redux'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <App /> </Provider> );//app.js import React, { Component } from 'react' import Count from './container/Count' export default class App extends Component { render() { return ( <div> <Count /> </div> ) } }
🍓文件优化
-
删除component文件
-
将UI组件和容器组件写到一起
//Count.jsx import React, { Component } from 'react' import { connect } from 'react-redux' import { createIncrementAction, createDecrementAction, createIncrementAsyncAction } from '../../redux/count_action' class Count extends Component { increase = () => { const value = this.selected.value; this.props.add(value * 1) } decrease = () => { const value = this.selected.value; this.props.minus(value * 1) } increaseIfOdds = () => { const value = this.selected.value; if (this.props.count % 2 !== 0) { this.props.add(value * 1) } } increaseIfAsync = () => { const value = this.selected.value; this.props.addAsync(value * 1, 500) } render() { return ( <div> <h1>当前求和为:{this.props.count}</h1> <select ref={(cNode) => { this.selected = cNode }}> <option value="1" >1</option> <option value="2" >2</option> <option value="3" >3</option> </select> <button onClick={this.increase}>+</button> <button onClick={this.decrease}>-</button> <button onClick={this.increaseIfOdds}>奇数再加</button> <button onClick={this.increaseIfAsync}>异步再加</button> </div> ) } } export default connect( state => ({ count: state }), { add: createIncrementAction, minus: createDecrementAction, addAsync: createIncrementAsyncAction } )(Count)
🍑总结
-
容器组件和UI组件整合一个文件
-
无需自己给容器组件传递store,给< App/>包裹一个< Provider store={store}>即可
-
使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作
-
mapDispatchToProps也可以简单的写成一个对象
-
一个组件要和redux“打交道”要经过那几步
-
定义好UI组件---不暴露
-
引入connect生成一个容器组件,并暴露,写法如下:
connect( state =>({key :value}){key : xxxxxAction} )(UI组件) -
在UI组件中通过this.props.xxxxxxx读取和操作状态
-
7.5.3 数据共享
redux如果为多个组件服务的时候,必须用对象的结构保存总状态,根据key保存value
combineReducers传入的对象就是redux帮我们保存的总对象
汇总所有的reducer变为一个总的reducer
//store.js
import { legacy_createStore as createStore, applyMiddleware, combineReducers } from "redux";
import thunk from 'redux-thunk'
import countReducer from './reducer/count'
import personReducer from './reducer/person'
const allReducer = combineReducers({
countReducer, personReducer
})
export default createStore(allReducer, applyMiddleware(thunk))
添加一个Person组件
🍌reducer/person.js
import { ADD_PERSON } from "../constant";
// 初始化person列表
const initState = [{ id: '001', name: 'tom', age: 18 }, { id: '002', name: 'jack', age: 20 }]
export default function personReducer(preState = initState, action) {
const { type, data } = action
switch (type) {
case ADD_PERSON:
return [data, ...preState]
default:
return initState
}
}
🍉constant.js和action/person.js
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'
import { ADD_PERSON } from "../constant";
// 准备一个动作对象
export const createAddPersonAction = (personObj) => ({ type: ADD_PERSON, data: personObj })
🍒container/person.jsx
import React, { Component } from 'react'
import { nanoid } from 'nanoid'
import { connect } from 'react-redux';
import { createAddPersonAction } from '../../redux/action/person'
class Person extends Component {
addPerson = () => {
let name = this.nameNode.value;
let age = this.ageNode.value;
const personObj = { id: nanoid(), name, age }
this.props.addPerson(personObj)
this.nameNode.value = ''
this.ageNode.value = ''
}
render() {
return (
<div>
<h1>我是Person组件</h1>
<input ref={(cNode) => this.nameNode = cNode} type="text" placeholder='请输入名字' />
<input ref={(cNode) => this.ageNode = cNode} type="text" placeholder='请输入年龄' />
<button onClick={this.addPerson}>添加</button>
<ul>
{
this.props.person.map((p) => {
return <li key={p.id}>姓名:{p.name}-----年龄:{p.age}</li>
})
}
</ul>
</div>
)
}
}
export default connect(
state => ({
person: state.person,
count: state.count
}),
{
addPerson: createAddPersonAction
}
)(Person)
7.6 redux开发者工具
下载工具依赖包
- npm install --save-dev redux-devtools-extension
在store.js文件中
import { composeWithDevTools } from 'redux-devtools-extension'
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))
7.7 纯函数和高阶函数
7.7.1 纯函数
-
一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
-
必须遵守以下一些约束
- 不得改写参数数据
- 不会产生任何副作用,例如网络请求,输入和输出设备
- 不能调用Date.now()或者Math.random()等不纯的方法
-
redux的reducer函数必须是一个纯函数
7.7.2 高阶函数
-
理解: 一类特别的函数
- 情况1: 参数是函数
- 情况2: 返回是函数
-
常见的高阶函数:
- 定时器设置函数
- 数组的forEach()/map()/filter()/reduce()/find()/bind()
- promise
- react-redux中的connect函数
-
作用: 能实现更加动态, 更加可扩展的功能
————————————本章笔记完成于2022.6.2————————————

浙公网安备 33010602011771号