zustand
type
status
date
slug
summary
tags
category
icon
password
Blocking
Blocked by
top
URL
Sub-item
Parent item
Provider + Context(useContext)
一个完整的例子
context 可以实现基础的状态共享。步骤:
- 通过createContext创建一个Context,并设置默认值。
- Context 中的值都在 Provider 的作用域下有效。所以在最外层包一个 Provider,value中设置传递值。
- 如果子组件需要时,则通过 React.useContext拿到Context值。
缺点:
- 重复渲染,性能差
当 Context 中的某个属性发生变化时,所有使用该 Context 的子组件都会被重新渲染,即使某些组件可能并未使用到这个属性。
这种问题有两种解决方法:
- React.memo 和 shouldComponentUpdate 这两种方式来解决。
- 将一个大型的 Context 拆分成多个小的 Context,可以精细地控制每个 Context 的更新,从而减少整个应用程序的重新渲染次数,提高性能和用户体验。
- 数据变更方法难以维护
为了保证数据变更方法的可维护性与 action 的不变性,有两种方法但各有问题:使用自定义 hooks,但为了不会重复渲染,每个修改方法需要使用useCallback,需要声明依赖,会造成极大的心智负担。使用useReducer,不支持异步函数、不支持内部的 reducer 互相调用,不支持和其他 state 联动(比如要当参数传进去才可用)。
- 使用范围限制
context的store只能在react中使用,无法在外部函数使用,例如请求函数中需要store中的属性,不能直接在请求函数中调用,就只能通过react中作为参数传递。
- 无法处理异步请求。
对于异步的逻辑,Context API并没有提供任何API,需要自己做封装。
- 无法处理数据间的联动
Context API并没有提供API来生成派生状态,同样也需要自行去封装一些方法来实现。
Redux
优点
- Redux轻量,生态丰富,可以结合流行的
redux-thunk
、redux-saga
等进行使用
- Redux的写法比较固定,团队应用中风格比较稳定,提高协作中的可维护性
- 因为Redux中的reducer更新时,每次return的都是不可变对象,所以时间旅行操作相对容易
缺点
- 要写大量的模板语言,一个操作定义action类型、而类型还是字符串,写对应ts类型,reducer定义里实现action的操作、store。
- 需要通过context providers包裹组件来实现重新渲染。
- 很多功能都不具备,很多功能需要引入中间件,例如异步、副作用。学习成本很高。
- 通过<Provider/> 单项数据流,没有数据向上回溯的能力,只能向下分发。
Zustand
优点
轻量级
与 Redux 相比,Zustand 的包大小更小。
状态共享
context需要在最外层包一个 Provider。 Context 中的值都在 Provider 的作用域下有效。而 zustand默认是单例模式, 不需要 Provider。直接声明一个 hooks 式的 useStore 后就可以在不同组件中进行调用。
如果需要多实例的话,zustand 也提供了对应的 Provider 的书写方式,
状态变更
- 函数可以直接写,完全不用区分同步或者异步。
- zustand 会默认将所有的函数保持同一引用。用 zustand 写的方法,默认都不会造成额外的重复渲染。
- 更好的状态内聚性,可以互相调用,不用再重新声明函数。
状态派生
- zustand 用了类似 redux selector 的方法,实现相应的状态派生。
- 可以实现只有监听的属性变化,才会重新渲染
状态组合
可以将相关的状态进行组合,结合中间件实现对单个状态组合进行一些操作,例如对状态组合实现持久化,缓存到localStorage
多环境集成
可以不在 react 环境内直接获取状态数据
性能优化
利用 zustand 的 selector,将得到的对象聚焦到我们需要的对象,只监听这几个对象的变化即可。
支持中间件
- devtools,可以将redux的devtools
- localstorage,可以直接缓存属性。