chenglong

zustand源码解读-上 - chenyuan

notion image

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 基本的使用方式和reduxuseSelector的一模一样

源码主体流程

zustand的核心是将外部store和组件view的交互,交互的核心流程如下图
notion image
先使用create函数基于注入的initStateCreateFunc创建一个闭包的store,并暴露对应的subscribesetStategetStatedestory(此 api 将被移除) 这几个api
借助于react官方提供的useSyncExternalStoreWithSelector可以将storeview层绑定起来,从而实现使用外部的store来控制页面的展示。
zustand还支持了middleware的能力,采用create(middleware(...args))的形式即可使用对应的middleware

核心代码详解

这部分讲解最核心的createuseSyncExternalStoreWithSelector函数

create 函数生成 store

前置知识介绍

create函数生成的store是一个闭包,通过暴露api的方式实现对store的访问。
核心代码在vanilla.tsreact.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,封装后支持了selectorisEqual
useSyncExternalStore一定需要传入subscribegetSnapshot两个函数,返回值是getSnapshot的返回结果。react会给subscribe注入一个callback函数,当外部store变化的时候,一定要手动的调用callback,通知react外部store变化了,需要它重新调用getSnapshot获取最新的状态,如果状态改变了就触发re-render,否则不re-render
useSyncExternalStoreWithSelector的优化主要是允许从一个大store中取出组件所用到的部分,同时借助isEqual来减少re-render的次数

Copyright © 2024 chenglong

logo