zustand原理深度剖析

14 阅读3分钟

zustand原理

众所周知,前端状态管理库有两种模式,基于函数式编程思想的redux,一种是基于响应式编程思想的mobx。如今最火并被称为redux继任者的zustand,运用了什么样的原理呢?

发布订阅者模式

首先我们说到发布订阅者模式 发布者 => 事件中心 => 【 订阅者1,订阅者2,订阅者3】 订阅者不知道是谁发布的,也不知道还有哪些订阅者,他们之间完全解耦。

这里看一下zustand是怎么使用的(官网例子)。

//创建仓库(事件中心)
import { create } from 'zustand' 

const useBear = create((set) => ({ 
	bears: 0, 
	increasePopulation: () => set((state) => ({ bears: state.bears + 1 })), 
	removeAllBears: () => set({ bears: 0 }), 
	updateBears: (newBears) => set({ bears: newBears }), 
}))
//订阅者
function BearCounter() {
  const bears = useBear((state) => state.bears)
  return <h1>{bears} bears around here...</h1>
}
//发布者
function Controls() {
  const increasePopulation = useBear((state) => state.increasePopulation)
  return <button onClick={increasePopulation}>one upbutton

我们可以看到zustand的核心是发布订阅者模式,那么react又是如何知道状态更改需要更新的呢?

useSyncExternalStore

官方文档 useSyncExternalStore 是一个让你订阅外部 store 的 React Hook。 虽然react推荐在内部使用react useContext和useReducer维护状态,但是如果你考虑在外部维护,可以使用useSyncExternalStorehooks。 所以zustand就干了两件事

  1. 可以生成一个发布订阅者模式的仓库的创建器
  2. 和react相关联

代码模拟

function createStore(createState) {
 
  let state;
  const listeners = new Set();//存放想听状态变化通知"的函数的集合
  const setState = (partial) => {
    const nextState = typeof partial === "function" ? partial(state) : partial;
    if (Object.is(nextState, state)) return;
    const previousState = state;
    state = Object.assign({}, state, nextState);
    listeners.forEach((listener) => listener(state, previousState));
  };//支持传递数据还是函数,如果是函数就把state作为入参传递过去

  const getState = () => state;

  const subscription = (listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener);

  };

  const api = { setState, getState, subscription };
  state = createState(setState, getState, api);// 执行用户的初始化函数
	return api;
}

export default createStore;

其中listeners是核心,主要在数据变化发布通知、组件订阅、取消订阅的时候使用 subscription作为添加订阅者和当组件卸载的时候删除订阅者。

import { useSyncExternalStore } from "react";
import createStore from "./vanilla";

function useStore(api, selector) {
//selector 可选择部分函数
  const getSnapshot = () => {
    const state = api.getState();
    return selector ? selector(state) : state;
  };
	//snapshot 当前瞬间的状态值
  return useSyncExternalStore(api.subscription, getSnapshot, getSnapshot);
}

//创建 React 可用的 store,返回一个"两用"的 Hook(既能订阅,又能直接操作)。
function create(createState) {
  const api = createStore(createState);
  const useBoundStore = (selector) => useStore(api, selector);
  Object.assign(useBoundStore, api);
  return useBoundStore;
}
  
export default create;

useStore利用useSyncExternalStore能够使store接入react。 此时就已经可以使用了

核心源码

type SetStateInternal<T> = {
  _(
    partial: T | Partial<T> | { _(state: T): T | Partial<T> }['_'],
    replace?: false,
  ): void
  _(state: T | { _(state: T): T }['_'], replace: true): void
}['_']

export interface StoreApi<T> {
  setState: SetStateInternal<T>
  getState: () => T
  getInitialState: () => T
  subscribe: (listener: (state: T, prevState: T) => void) => () => void
}

export type ExtractState<S> = S extends { getState: () => infer T } ? T : never

type Get<T, K, F> = K extends keyof T ? T[K] : F

export type Mutate<S, Ms> = number extends Ms['length' & keyof Ms]
  ? S
  : Ms extends []
    ? S
    : Ms extends [[infer Mi, infer Ma], ...infer Mrs]
      ? Mutate<StoreMutators<S, Ma>[Mi & StoreMutatorIdentifier], Mrs>
      : never

export type StateCreator<
  T,
  Mis extends [StoreMutatorIdentifier, unknown][] = [],
  Mos extends [StoreMutatorIdentifier, unknown][] = [],
  U = T,
> = ((
  setState: Get<Mutate<StoreApi<T>, Mis>, 'setState', never>,
  getState: Get<Mutate<StoreApi<T>, Mis>, 'getState', never>,
  store: Mutate<StoreApi<T>, Mis>,
) => U) & { $$storeMutators?: Mos }

// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-object-type
export interface StoreMutators<S, A> {}
export type StoreMutatorIdentifier = keyof StoreMutators<unknown, unknown>

type CreateStore = {
  <T, Mos extends [StoreMutatorIdentifier, unknown][] = []>(
    initializer: StateCreator<T, [], Mos>,
  ): Mutate<StoreApi<T>, Mos>

  <T>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
    initializer: StateCreator<T, [], Mos>,
  ) => Mutate<StoreApi<T>, Mos>
}

type CreateStoreImpl = <
  T,
  Mos extends [StoreMutatorIdentifier, unknown][] = [],
>(
  initializer: StateCreator<T, [], Mos>,
) => Mutate<StoreApi<T>, Mos>

const createStoreImpl: CreateStoreImpl = (createState) => {
  type TState = ReturnType<typeof createState>
  type Listener = (state: TState, prevState: TState) => void
  let state: TState
  const listeners: Set<Listener> = new Set()

  const setState: StoreApi<TState>['setState'] = (partial, replace) => {
    // TODO: Remove type assertion once https://github.com/microsoft/TypeScript/issues/37663 is resolved
    // https://github.com/microsoft/TypeScript/issues/37663#issuecomment-759728342
    const nextState =
      typeof partial === 'function'
        ? (partial as (state: TState) => TState)(state)
        : partial
    if (!Object.is(nextState, state)) {
      const previousState = state
      state =
        (replace ?? (typeof nextState !== 'object' || nextState === null))
          ? (nextState as TState)
          : Object.assign({}, state, nextState)
      listeners.forEach((listener) => listener(state, previousState))
    }
  }

  const getState: StoreApi<TState>['getState'] = () => state

  const getInitialState: StoreApi<TState>['getInitialState'] = () =>
    initialState

  const subscribe: StoreApi<TState>['subscribe'] = (listener) => {
    listeners.add(listener)
    // Unsubscribe
    return () => listeners.delete(listener)
  }

  const api = { setState, getState, getInitialState, subscribe }
  const initialState = (state = createState(setState, getState, api))
  return api as any
}

export const createStore = ((createState) =>
  createState ? createStoreImpl(createState) : createStoreImpl) as CreateStore