参考资料:
1. redux
React有props和state:
- props意味着父级分发下来的属性
- state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,这就是react的单向数据流
这就意味着如果是一个数据状态非常复杂的应用,更多的时候发现React根本无法让两个组件互相交流,使用对方的数据,react的通过层级传递数据的这种方法是非常难受的,
在项目,我们往往会有这样的需求,根据当前登录的用户的信息展示不同的菜单或者数据,这就需要我们在任意组件中能获取到登录用户的信息,要怎么做呢?如果没有这些框架,我们肯定会想办法将这些全局的信息统一存储到一个地方,设计好对应的数据结构存在一个 js 对象中,并且这个对象必须 单例 的,保证每次获取的数据都是同一个。对于修改逻辑,也要遵循既定的规则,保证每次数据修改都是可追溯的。如果没有这些规则的约定,那么状态(数据)的修改变得混沌无序,代码变得乱七八糟,系统潜在的问题增多。
这个时候,迫切需要一个机制,把所有的state集中到组件顶部,能够灵活的将所有state各取所需的分发给所有的组件,是的,这就是redux
(1)redux机制介绍
-
actions
函数形式,返回 action 对象,通常具有type属性。负责指令的生成,页面通过 store.dispatch(action) 向 store 发送数据修改的请求。
-
reducers
一个纯函数,接收两个参数 (previousState, action) 第一个表示修改之前的 state 的值,action 是上一步页面通过 store.dispatch(action) 向 store 传递的 action。 reducers 通过 action 的 type 的值进行不同的处理,返回一个新的变量 newState。reducers不能直接修改传递进来的 previousState.
-
store
通过 const store = createStore(reducer); 创建一个 store 对象。 需要导入到 React 组件文件中,通过 this.state = store.getState(); 让组件获取 store 中的数据;
整个过程就是 redux 的三大原则:
- 单一数据源: store 中数据是单例,全局唯一的。
- State 是只读的: 唯一改变 state 的方法就是触发 action, store.dispatch(action); 直接修改 state 是不起作用的。
- reducers 是纯函数 保证在(previousState, action)确定的前提下,多次执行的结果是一样的;并且不直接修改 state ,通过 Store 触发 state 的修改。
(2)基本使用
-
创建reducer
可以使用单独的一个reducer,也可以将多个reducer合并为一个reducer,即:combineReducers() action发出命令后将state放入reucer加工函数中,返回新的state,对state进行加工处理
-
创建action
用户是接触不到state的,只能有view触发,所以,这个action可以理解为指令,需要发出多少动作就有多少指令action是一个对象,必须有一个叫type的参数,定义action类型
-
创建的store,使用createStore方法
store 可以理解为有多个加工机器的总工厂 提供subscribe,dispatch,getState这些方法。
2. react-redux
react-redux 是将 react 和 redux 结合,其核心思想和机制依然不变
(1)设置action和actionType
aciton负责定义行为并且派发改变的数据和行为给store,store通过store.dispatch再发给reducer,reducer进行行为识别和数据的接收
这里我们将actionType和aciton进行统一管理。
actionType.js:
aciton.js
type表示当前action目的是用来做什么的,以此来区分不同的改变store数据的行为(比如增加money金额)
data表示需要目标aciton行为需要携带的参数或者说是需要修改的具体数值(比如增加500块的money金额)
(2)设置reducer
reducer负责根据指定action的区分来对指定的state进行处理。
一般来说每一个reducer都是一个纯函数,代表一种state数据。其默认有两个参数(当前state的值,传过来的action)。
你也可以只需要一个reducer函数来处理所有的aciton,此时的state应该是个存储所有State的对象。每次只更新对象中指定的state
其中当前state一般会赋值一个初始默认值,然后后续每次的更新都会基于preState的值进行处理,并返回一个逻辑处理后的newState值。然后交给store进行对应state更新
reducer.js
(3)创建store并注入全局
只有store才是负责更新state的,并且reducer必须是个纯函数;reducer负责进行处理逻辑并返回一个newstate给store,然后store再进行自我更新;
store必须是唯一的,不允许存在多个store
store.js
将store.js暴露出去后,传给顶层组件App.js的props后,在顶层组件内部,引入react-redux的Provider并传入prop store
App.js(顶层组件) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bW9jIvly-1622874605459)(p3-juejin.byteimg.com/tos-cn-i-k3…)]
(4) 在组件中使用Store的数据和更新Store数据
需要用到以下三个函数:
- mapStateToProps 函数,将 store 中的 state 转为组件的 props
- mapDispatchToProps 函数, 传入 dispatch 用于触发 action。
- connect 函数(本质上对应的是redux的Store.subscribe)。
const mapStateToProps = (state)=>({
inputValue: state.inputValue,
list: state.list
})
const mapDispatchToProps = (dispatch) => ({
hanldeAdd: ()=>{
dispatch(addItem());
},
changeInputValue: (e)=>{
dispatch(changeValue(e.target.value));
},
deleteItem: (key)=>{
dispatch(deleteItem(key))
}
})
// 接收 mapStateToProps 和 mapDispatchToProps 两个参数,
//并返回一个函数,这个函数接受 TodoList 返回一个封装的组件。
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
(5) redux-thunk(异步中间件)
如果按照原始的redux工作流程,当组件中产生一个action后会直接触发reducer修改state,reducer又是一个纯函数,也就是不能再reducer中进行异步操作;
而往往实际中,组件中发生的action后,在进入reducer之前需要完成一个异步任务,比如发送ajax请求后拿到数据后,再进入reducer,显然原生的redux是不支持这种操作的
redux提供了redux-thunk这个中间件可以让我们actions 和 reducers 之间 “ 动一些手脚 ”,先处理异步的请求,再去触发 dispatch 向 store 发起操作。
在store中加入中间件redux-thunk:
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducer from "../reducers";
const store = createStore(reducer, applyMiddleware(thunk));
export default store;
在某个aciton中使用异步:
// actionCreator.js
export const getInitList = ()=> {
return function(dispatch){
axios.get("/api/initList.json").then(res=>{
dispatch(initList(res.data))
})
}
}