1. 什么是状态管理
1.1. 简介
从React诞生之后,前端组件化的方案深入人心,React遵循的是单向数据流的原则,属性通过Props自上而下的传递。
当页面的比较简单,组件之间的层级关系比较浅时,这种自上而下的单向数据流的方式是不会有问题的。如果页面一复杂,组件的嵌套层级一深,这种单向数据流的传递方式,将会使你陷入到“嵌套地狱”。
状态管理本身,解决的就是这种“嵌套地狱”的问题,解决的是跨层级组件之间的数据通信和状态共享。
1.2. 状态管理工具的本质: 管理共享内存中的状态
1.共享内存
2.管理状态
3.页面通信
4.组件通信
5.刷新失效?
详细定义:单页应用的各个组件本身是共享内存的,如果将状态保存在内存中就可以读写统一内存中的变量,从而达到状态共享的目的
1.3. 为什么React有这么多状态管理工具?
Vue: Vuex(Pinia)
Angular: Service和Rxis
React: Flux、Redux、Mobx、Rxis、Recoil、 Jotai、 Zustand
跟不同前端框架的定义有关,Vue和Angular双向数据绑定,计算属性等,数据是响应式的控制视图刷新,拥有计算属性等,这些使得Vue和Angular需要状态管理的场景减少,此外其本身就包含了完整的状态管理工具,比如Vue的Vuex和Pinia,Angular的Service(RXis)等,从官方定调。而React不一样,React是一个纯UI层的前端框架,Ul = fn(state),React将状态的变动完全交给开发者。
2. React状态管理简介
3. 实现一个简易的状态管理工具
基于发布/订阅模式,实现一个简单的store:
export default class CreateStore {
constructor(reducer, initialState) {
this.currentReducer = reducer;
this.currentState = initialState;
this.listeners = [];
this.isDispatching = false;
}
getState() {
return this.currentState;
}
subscribe(listener) {
this.listeners.push(listener);
return function unsubscribe() {
var index = this.listeners.indexOf(listener);
this.listeners.splice(index, 1);
}
}
dispatch(action) {
try{
this.isDispatching = true;
this.currentState = currentReducer(currentState, action);
} finally {
this.isDispatching = false;
}
this.listeners.slice().forEach(listener => listener());
return action;
}
}
接着使用这个CreateStore来创建一个全局的状态
import CreateStore from './CreateStore.js'
function todos(state = [], action) {
switch(action.type) {
case 'ADD_TODO':
retrun state.concat([action.text])
default:
return state
}
}
const store = createStore(todos, ['Use Redux'])
store.dispatch({
type: 'ADD_TODO',
text: 'Read the docs'
})
console.log(store.getState())
// [ 'Use Redux', 'Read the docs' ]
也可以通过 Store.subscribe 来监听状态变化,重新渲染 React UI 层等
4. Redux在项目中的实践
4.1. 如何使用Redux
首先要明确为什么要使用redux,这一点很重要,如果不知道为什么使用redux,那么在开发的过程中肯定不能合理的使用redux
首先来看redux的本质:redux做为一款状态管理工具,主要是为了解决组件间通信的问题
既然是组件间的通信问题,那么显然将所有页面的状态都放入redux中,是不合理的,复杂度也很高
减少局部状态和redux状态的不合理混用:
全量使用redux的复杂度很高,我们当然考虑将一部分状态放在redux中,一部分状态放在locastate中,但是这种情况下,很容易产生一个问题,就是如果local State跟redux中的state存在状态依赖
4.2. Redux复杂的模版代码
redux是遵循函数式编程的规则,上述的数据流中,action是一个原始js对象 (plain object) 且 reducer 是一个纯函数,对于同步且没有副作用的操作,上述的数据流起到可以管理数据,从而控制视图层更新的目的
如果存在副作用函数,那么我们需要首先处理副作用函数,然后生成原始的is对象。如何处理副作用操作,在 redux 中选择在发出 action,到 reducer 处理函数之间使用中间件处理副作用
在有副作用的action和原始的action之间增加中间件处理,从图中我们也可以看出,中间件的作用就是:转换异步操作,生成原始的action,这样,reducer函数就能处理相应的action,从而改变state,更新UI
因为中间件,纯函数Reducer等使得Redux需要写很多样板代码,使用起来越来越复杂,早期我们使用redux-thunk,或者redux-saga等,但是复杂度还是在那里,因此在项目中不推荐使用如此复杂的Redux以及相关逻辑。
4.3. Redux toolkit
用redux需要有太多的样板代码,中间件代码等等,还需要区别同步和异步操作,及其复杂。早期Dvais通过封装,能够解决部分上述的问题。早期的Redux,我们也需要引入很多Redux相关的包,比如React-redux等等,显得复杂而繁琐,而Redux toolkit的出现则是完全解决了上述的问题,使得Redux的开发可以简单明了。
Redux toolkit是官方推荐的高效的redux状态管理工具集。Redux toolkit可以简化Redux开发,包括配置 store、定义 reducer,不可变的更新逻辑、甚至可以立即创建整个状态的“切片 slice",而无需手动编写任何 action creator 或者 action type。此外,Redux toolkit提供了完整的React的hooks,可以方便React函数组件中使用Redux toolkit。