各类React状态管理工具介绍 | 青训营笔记
1、Local State(props)
顾名思义,就是组件级别的局部状态:
import {useState} from 'react'
const Hello = () => {
const [name,setName] = useState('Jony')
return <>Hello,{name}</>
}
这个local State只能在当前的Hello组件中生效,当组件创建时初始化和生效,组件销毁时失效。
之所以它也能被称为状态管理,是因为我们可以通过props进行层层传递来达到共享的效果,这是最繁琐的一种方式。
但同级组件之间不能使用props怎么办呢?
我们可以将localState向上提一级,包裹要传递状态的两个组件,通过这个父组件来实现他们之间的状态互通
但这终究不是长久之计,如果组件之间差的层级太多,这个方法就不太实用了。
为解决这个问题,我们引出React本身提供的Context:
2、Context
Context就像是一个单独的数据库一样,所有组件都可以从其中读取和写入数据,这样就达到了状态全局共享的目的
创建及注入:
render() {
return (
//使用Provider进行context全局注入
//这里的value表示对context重新赋值
<GlobalContext.Provider value={{name:'scw1',onClick:this.handleClick.bind(this)}}>
<B />
</GlobalContext.Provider>
)
}
使用:
render() {
return <GlobalContext.Consumer>
{(globalContext)=>
//消费Context的数据
<span onClick={globalContext.onClick}>
{globalContext.name}
</span>
}
</GlobalContext.Consumer>
Context的问题:
- Context相当于全局变量,难以追溯数据的变更情况
- 使用Context的组件内部耦合度太高,不利于组件的复用和单元测试
- 会产生不必要的更新(会穿透memo和dependencies等)
- Context只能存储单一值,无法存储多个各自拥有消费者的值的集合
- 粒度也不好控制,不能细粒度的区分组件依赖了哪一个Context
- 多个Context会存在层层嵌套的问题
一些简单的数据,比如主题、语言等设置不会频繁的去变更,就可以放在context中进行保存
3、Flux
是Facebook官方给出的应用架构,利用单向流动的形式对公共状态进行管理,不过现在Flux已经被淘汰了,不过其设计思想还是可以参考和借鉴的
架构图:
- view:视图层
- Action:视图发出的消息
- Dispathcer:派发者,用来接收Action,执行回调函数
- Store:数据层,存放状态,一旦发生改动,就会更新数据以及emit相关事件等
因为它耦合的层级比较深,所以redux诞生没多久就将其替代了
4、Redux
Redux从Flux演变而来,由于提出年份较早,不可避免地要兼容类组件写法,导致了其本身较重一些
架构图:
Redux首先解构了Action和Dispatcher,使Action独立出来,不像Flux中Action需要和Dispatcher耦合在一起
遵循三大原则:
- 单一数据源
- Store中的state只读
- 使用纯函数来执行修改
正是因为上述的特性,使得Redux可以做时间旅行,可以让应用程序切换到任意时间的状态。因此,如果复杂的场景,特别是存在页面组件间复杂的通信的场景,就非常适合使用Redux来管理状态!
但Redux的缺点也很明显:
- 为实现纯函数的Reducer,Redux必须处理各种各样的副作用,需要引入一系列的副作用中间件,加重了心智负担
- Action、Dispatch、Reducer的模式需要写过多的样板代码,增加了复杂度
因此中小型项目,不太推荐使用Redux
5、Mobx
Mobx通过透明的函数响应式编程使得状态管理变得简单和可扩展,设计和vue较为相似,是一个响应式的状态管理库
Mobx借助于装饰器的实现,使得代码更加简洁易懂。由于使用了可观察对象,所以Mobx可以做到直接修改状态,而不必像Redux一样编写繁琐的actions和reducers
优点:
- 上手简单,可以直接修改状态不需要编写繁琐的actions和reducers
缺点:
- 不能实现时间旅行和回溯,不适合前端数据流比较复杂的场景
随着React hooks,比如UserReducer等,以及原子型状态管理工具Recoil的发展。Mobx的的使用场景已经越来越小。
6、Recoil
Recoil一定程度上解决了Local State和Context的局限性,且能够兼容React的新特性,比如Concurrent模式等。
其解决的问题:
- 组件间的状态共享只能通过将state提升至它们的公共祖先来实现,但这样做可能导致渲染一颗巨大组件树
- Context只能存储单一值,无法存储多个各自拥有消费者的值的集合
Recoil更加的具有原子性,其中的状态都是Atom,可以任意组合,如下红色的是需要重新渲染的:
如果我们使用context:
使用Recoil渲染量一下子就少了很多
7、Zustand
Zustand是主打轻量级的状态管理工具,没有Redux那样臃肿的设计,也没有React类组件的历史包袱,它的体积很小,因此很适合做移动端网页
且Zustand的使用极其简单,在初始化的过程中,我们不仅能保存状态,也能在初始化的时候制定方法和函数