手写zustand
阅读时间: 4分钟 21秒
介绍
Zustand是一个开源的React状态库,可以作为Redux的替代品。具有比redux更简洁的语法和更灵活的使用方法
两者对比: 

实现
const createStore = (createState) => {
let state // store内部状态存储于state上
const getStore = () => state
const setStore = () => {} // setState就是create接收函数的入参
const subscribe = () => {} // 每次订阅时将subscribe加入到listeners,subscribe的作用是触发组件重新渲染
const api = { getStore, setStore, subscribe }
state = createState(setStore) // state的初始值就是createState的调用结果
return api
}
const useStore = (api, selector, equalityFn) => {}
export const create = (createState) => {
const api = createStore(createState) // 拿到store,包含了全部操作store的方法
const useBoundStore = (selector, equalityFn) =>
useStore(api, selector, equalityFn)
return useBoundStore
}
- createStore 用来创建 Store,内部维护了 Store 的状态以及操作 Store 的函数 API,其中包括了:
- 获取状态函数 getStore;
- 设置状态函数 setStore;
- 订阅函数 subscribe,组件会订阅这个 Store,当状态发生改变时会重新渲染。
- useBoundStore 接收 selector(从完整的状态中选取部分状态),equalityFn(用来对比选取状态是否发生变化,从而决定是否重新渲染)。
- useStore 借助 useSyncExternalStoreWithSelector 完成订阅、状态选取、re-render 优化,返回选取的状态。
- create 完成上述函数的组合。
其中通知react状态更新使用的api是useSyncExternalStoreWithSelector,其底层是useSyncExternalStore
最终代码:
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector' // 内部是useSyncExternalStore,useSyncExternalStoreWithSelector在其基础上增加了memorized,用selector防止re-render
type Subscribe = Parameters<typeof useSyncExternalStoreWithSelector>[0]
type GetState<T> = () => T
type SetState<T> = (
partial: T | Partial<T> | ((state: T) => T | Partial<T>)
) => void
type StoreApi<T> = {
setState: SetState<T>
getState: GetState<T>
subscribe: Subscribe
}
type StateCreator<T> = (setState: SetState<T>) => T
type EqualityFn<T> = (a: T, b: T) => boolean
/**
* `createStore` 用来创建Store
*/
const createStore = <T>(createState: StateCreator<T>): StoreApi<T> => {
const listeners = new Set<(state: T) => void>()
let state: T // store内部状态存储于state上
const setState: SetState<T> = (partial) => {
// setState就是create接收函数的入参
const nextState =
typeof partial === 'function'
? (partial as (state: T) => T)(state)
: partial
if (!Object.is(nextState, state)) {
state =
typeof nextState !== 'object' || nextState === null
? (nextState as T)
: Object.assign({}, state, nextState)
// 当状态发生变化时,依次通知组件re-render,也就是循环调用一遍listeners的所有函数
listeners.forEach((listener) => listener(state))
}
}
const getState = () => state
const subscribe: Subscribe = (subscribe) => {
// 每次订阅时将subscribe加入到listeners,subscribe的作用是触发组件重新渲染
listeners.add(subscribe)
return () => listeners.delete(subscribe)
}
const api = { setState, getState, subscribe }
state = createState(setState) // state的初始值就是createState的调用结果
return api
}
/**
* `useStore` 借助 `useSyncExternalStoreWithSelector` 完成订阅、状态选取、re-render优化,返回选取的状态
*/
const useStore = <State, StateSlice>(
api: StoreApi<State>,
selector: (state: State) => StateSlice = api.getState as any,
equalityFn?: EqualityFn<StateSlice>
) => {
const slice = useSyncExternalStoreWithSelector(
api.subscribe,
api.getState,
api.getState,
selector,
equalityFn
)
return slice
}
export const create = <T>(createState: StateCreator<T>) => {
const api = createStore(createState) // 拿到store,包含了全部操作store的方法
const useBoundStore = <TSlice = T>(
selector?: (state: T) => TSlice,
equalityFn?: EqualityFn<TSlice>
) => useStore<T, TSlice>(api, selector, equalityFn)
Object.assign(useBoundStore, api)
return useBoundStore as typeof useBoundStore & StoreApi<T>
}
使用
import { create } from './my-zustand'
export interface TodoItem {
id: string
todo: string
status: string
}
interface TodoState {
todoList: TodoItem[]
}
interface TodoAction {
setTodoList: (fn: (list: TodoItem[]) => TodoItem[]) => void
}
export const useTodoStore = create<TodoState>(() => ({
todoList: [] as TodoItem[],
// setTodoList(fn) {
// set((prev) => ({ todoList: fn(prev.todoList) }))
// },
}))
export const setTodoList = (fn: (list: TodoItem[]) => TodoItem[]) => {
useTodoStore.setState((prev) => ({ todoList: fn(prev.todoList) }))
}
// setInterval(() => {
// setTodoList(
// produce((list) => {
// list.pop()
// })
// )
// }, 3000)