为什么我们需要状态管理库呢?
学习redux不能只学redux,事实上我们学习的是一种前端状态管理的思维
目前前端状态管理库一般都是基于Flux架构,redux,zustand,pinia,mobx….未来一定还会有无穷多的新轮子出现,我们只有知道为什么需要状态管理,才能更好的学习。
我们知道react是state直接渲染成dom的,
而redux的做法,是把全局的状态都放在一个集成的store里来管理,这个store是全局唯一的,负责来提供整个应用的state,所有函数的组件都可以放在函数外部的store里,从而由store中的状态的树形关系映射到组件的树形关系上。
这使得组件的通信更加容易
在不使用全局管理开发时,要厘清各个组件的状态传递是非常痛苦的,依赖各种传递技巧,你需要不停的思考状态的追踪。父组件的状态需要通过props来通知子组件,而子组件发生变化了之后,需要一个事件暴露出去才能使得父组件感受到他的变化,而兄弟组件通信就更加麻烦了。
有了redux之类的状态管理之后,组件间的通信全都通过store的中转来完成,组件间的相互通信频率就会降低,当store变化,组件能拿到store里的数据,当你的store状态需要变化就useAction去更新store。
redux特性1:唯一数据来源
所有的view的状态来源与store,组件内部尽量不要有自己的state。
store发生更新,view的渲染发生变化;用户交互更改store,触发事件去更新store;
redux特性2:可预测的
state+action=new state
action后会生成一个新的state而不是在原有的state上修改
redux特性3:纯函数更新store
纯函数:没有effect的函数组件,输出结果完全取决于输入参数,完全不依赖外部的参数。
通过action来触发事件,通过reducer的函数去产生store
虽然说是更新store,但是其实本质上是产生了新的store
理解redux的核心:store action reducer
Store
state是真正的数据,dispatcher是用来dispatch一个函数的方法,reducer来处理这个方法,斌且负责更新store。
#createStore
产生Store只需要调用一个简单的creatStore函数,参数是reducer(一般是合并的reducer)
const Store =createstore(reducer)
store的三个主要方法
1.getState()
2.dispatch(action)
用户产生了一个action,store就dispatch出去这个action,给到reducer
3.subscribe(listener)
监听store的变化,当store变化时 ,则调用listener回调
这里可以订阅store的变化,然后触发后面的log回调。
Action
action就是一个行为对象,里面的type键值对记得要写,方便给reducer辨明你是哪个
//reducer
function reducer(state=initialState,action){
switch(){
case 'PLUS'://要处理action 的type
return Object.assign({},state,{
todos:[...state.todos,{
text:action.text,
completed:false
}]
})
default:return state
}
}
//action Creator如下
//其实就是负责生成一个action 对象
function Plus(x){
return
{
type:'PLUS',
payload:{x}//action函数可以有负载参数
}
}
store.dispatch(Plus(5))
bindActionCreator
就是把createAction和dispatch的操作合成了
合并成下面一行,bindActionCreators接收Plus这个actionCreator和dispatch实现自动化生成并dispatch action
Plus=bindActionCreators(Plus,store.dispatch)
Reducer
reducer函数接收两个参数,初始状态initialState(也就是store里的默认初始值,一版来说传入的是待处理的state,如果没有,那需要去自定义一个initialstate)和用户的action。
随后在reducer函数中去判断这个用户触发的事件action该怎么处理,值得注意,一个action通过dispatcher 被dispatch出去后,所有你定义的reducer都会接收到,接收到后通过action.type来判断是否执行,执行也就是生成一个新的store,看上面也就是使用Object.assign(),传入一个空对象,旧state,之后…展开原先store的内容,下面做出更新store的操作。这样之后store上的数据改变了,绑定了store的组件都能订阅更新。
(state,action)=>new state
combineReducers
combineReducers把一个由多个不同 reducer 函数作为 value 的object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。
合并后的 reducer 可以调用各个子 reducer,并把它们的结果合并成一个 state 对象。state 对象的结构由传入的多个 reducer 的 key 决定。
//reducer一号
export default function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}
//reducer二号
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
import { combineReducers } from 'redux'
import todos from './todos'
import counter from './counter'
export default combineReducers({
todos,
counter
})
注:在 reducer 层级的任何一级都可以调用 combineReducers。并不是一定要在最外层。实际上,你可以把一些复杂的子 reducer 拆分成单独的孙子级 reducer,甚至更多层。
组件和store绑定——connect
组件需要获取store里的数据,组件里的操作也需要更新store,所以我们需要将组件和state绑定,使用connect方法//////////////////////////////////////////////////
connect()的参数
mapStateToProps(state)
一个基本的性能优化,也就是connect的第一个可选参:return的对象里包裹了这个组件需要使用到的state(state是reducer的返回值,即所有state),而不是全部一起把state传给组件;这里只绑定count,提升性能,把访问的ui结点限制到了最小的范围。否则state里的所有数据变化都会引起ui的变化
mapDispatchToProps(dispatch)
也就是把action绑定到组件上面,在组件上就可以用actions去获取所有redux的action了
connect的使用
Redux中间件(Middlewares)
当你的组件触发之后执行的action对象里发送了ajax请求、如何处理res的,action到dispatcher之前,先到达中间件,中间件来截获某种类型的action,接收到之后去访问一个Api,根据返回的结果,如果是成功则发一个成功的action。也就是说middleWares接受到这些action之后会进行一个预处理,之后才把真正的action dispatch给reducer,接着更新store。
优雅的管理Action和Reducer
不做管理的一些弊端
—src
——actions
———xxx.js
———xxxx.js
——reducers
———xxx.js
———xxxx.js
1.所有action放在同一个文件,太难管理
2.Action Reducer文件分开,实现业务逻辑时需要来回切换
3.系统中到底有多少action不是很直观
建议的做法/当然更建议的是转toolkit,绷
每一个action写成一个文件,reducer跟在下面
再开一个Redux文件夹,统一导入Action和Reducer
优势
后话:Redux官网强烈推荐Redux-Toolkit
即将推出toolkit的文档….