This article discusses the zustand v4.3.8 library for state management, which is based on the publish-subscribe pattern and can be used outside of React projects. The create function generates a store, which is a closure with exposed APIs for accessing the store. The useSyncExternalStoreWithSelector function binds the store to the view layer, allowing external stores to control page display. The article provides code examples for using zustand in JavaScript and TypeScript projects.
使用方式
zustand
是基于发布订阅模式实现的一个状态管理库,可以不局限于仅在react
项目中使用,不过对react
的支持是官方实现的,使用起来也非常简洁,使用示例如下如上文代码,在调用
create
函数后,会生成一个useStore
的 hook,这个 hook 基本的使用方式和redux
的useSelector
的一模一样源码主体流程
zustand
的核心是将外部store
和组件view
的交互,交互的核心流程如下图先使用
create
函数基于注入的initStateCreateFunc
创建一个闭包的store
,并暴露对应的subscribe
、setState
、getState
、destory
(此 api 将被移除) 这几个api
借助于
react
官方提供的useSyncExternalStoreWithSelector
可以将store
和view
层绑定起来,从而实现使用外部的store
来控制页面的展示。zustand
还支持了middleware
的能力,采用create(middleware(...args))
的形式即可使用对应的middleware
核心代码详解
这部分讲解最核心的
create
和useSyncExternalStoreWithSelector
函数create 函数生成 store
前置知识介绍
create
函数生成的store
是一个闭包,通过暴露api
的方式实现对store
的访问。核心代码在
vanilla.ts
和react.ts
这两个文件中,vanilla.ts
里实现了一个完整的有pub-sub
能力的store
, 不需要依赖于react
即可使用。react.ts
里基于useSyncExternalStoreWithSelector
实现了一个useStore
的 hook,在组件里调用create
返回的函数时会将store
和组件绑定起来,而这个绑定就是useStore
实现的这个
useSyncExternalStoreWithSelector
会在下一小节讲述。create 运行流程
在
create
函数调用的时候,先使用vanilla.ts
导出的createStore
生成store
,然后定义一个useBoundStore
函数,返回值是useStore(api, selector, equalityFn)
,然后把createStore
返回的api
注入useBoundStore
上,然后返回useBoundStore
.这个
useBoundStore
的使用方式和useSelector
一模一样简化带注释源码
useSyncExternalStoreWithSelector 解析
zustand
的核心代码如此简洁,一大原因就是使用了useSyncExternalStoreWithSelector
,这个是react
官方出的use-sync-external-store/shim/with-selector
包,之所以出这个包,是因为react
在提出useSyncExternalStore这个 hook 后,在react v18
版本做了重新实现,有破坏性更新。为了兼容性考虑出了这个包。话不多说,上源码
这个实现其实是基于官方的
useSyncExternalStore
做的一个封装,官方 hook 不支持传入selector
,封装后支持了selector
和isEqual
。useSyncExternalStore
一定需要传入subscribe
和getSnapshot
两个函数,返回值是getSnapshot
的返回结果。react
会给subscribe
注入一个callback
函数,当外部store
变化的时候,一定要手动的调用callback
,通知react
外部store
变化了,需要它重新调用getSnapshot
获取最新的状态,如果状态改变了就触发re-render
,否则不re-render
useSyncExternalStoreWithSelector
的优化主要是允许从一个大store
中取出组件所用到的部分,同时借助isEqual
来减少re-render
的次数