乐趣区

关于react.js:zustand状态管理源码解析一

对于 react 状态管理工具库有很多,比拟闻名的有 redux、mobx,这两款工具库应用相对来说比拟宽泛,明天咱们所要理解的是一个比拟小而精美的状态管理工具库 zustand,截止目前为止有 17+ 的 start,最次要的是这个库提供的 store 写法简略,不在像以往咱们通过 state、action、dispatch 等等一系列繁琐的操作进行状态治理,同时也能够脱离组件应用,让咱们一起读一读源码理解一下他的状态管理机制;

  本次咱们先从 3.x 版本解读,4.x 版本作者还在 rc 阶段,等最终公布后咱们在持续追加 4.x 版本的改变解析;

1、create 创立 store,与应用

  这个工具库相对来说很简略,咱们间接应用

import create from 'zustand'

interface BearStore {
  bears: number,
  increasePopulation: () => void,
  removeAllBears: () => void}

const useBearStore = create<BearStore>(set => ({
  bears: 0,
  increasePopulation: () => set(state => {console.log('用于 debug')
    return {bears: state.bears + 1}
  }),
  removeAllBears: () => set({ bears: 0})
}))

我的项目中的应用方法

import useBearStore from './BearStore'

const Home: React.FC = () => {const bearStore = useBearStore()
  return <div>
    内容信息
    <div>{bearStore.bears}</div>
    <button onClick={bearStore.increasePopulation}> 减少 </button>
    <button onClick={bearStore.removeAllBears}> 重置 </button>
  </div>
}

  在应用咱们对于 create 函数的应用做一个具体的阐明,咱们解读一下源码对于 create 函数的形容

// 源码对于 create 函数的阐明,const create = (<T extends State>(createState: StateCreator<T, [], []> | undefined) => {return createState ? createImpl(createState) : createImpl
}) as Create
// createState 参数定义阐明
export type StateCreator<
  T extends State,
  Mis extends [StoreMutatorIdentifier, unknown][] = [],
  Mos extends [StoreMutatorIdentifier, unknown][] = [],
  U = T
  > = ((
    setState: Get<Mutate<StoreApi<T>, Mis>, 'setState', undefined>,
    getState: Get<Mutate<StoreApi<T>, Mis>, 'getState', undefined>,
    store: Mutate<StoreApi<T>, Mis>,
    $$storeMutations: Mis
  ) => U) & {$$storeMutators?: Mos}

  对于 createState 类型定义的形容有很多,本期简略的讲用法;

  create咱们能够传递一个函数类型的参数,外面蕴含了 (set, get, other) => useStore,返回一个 useStore 的 hooks,在 react 组件中通过useStore(selector) 能够选择性的获取对应 store 切片,上面咱们就看看这些是怎么下面所讲的内容是怎么实现;

2、理解源码 create 过程

  通过一系列的源码剖析与学习,我这边大略总结了一张 create 执行过程的图解,通过这个图,咱们一步步剖析他的实现过程,参考图如下:

简略的来说分为以下几个步骤:

  • 1、通过 create 形式传入一个构建 store 的办法(set, get) => ({xxxx})
  • 2、在 create 函数外部,通过 createStore 创立一个状态治理 store(艰深点来说就是闭包);
  • 3、createStore 创立 store 以外,还返回上面内容

    • 返回 setState 办法,setState 办法更新值时候,去遍历 listener 订阅事件列表;
    • 返回 getState 办法,去获取 store 对应值;
    • 返回 subscribe 注入订阅事件的办法;
    • 返回 destroy 清空订阅事件列表办法;
  • 4、在 create 办法体内,持续创立一个 useStore hooks,返回给 react 组件去获取 store,对于 useStore 次要解决了上面几件事件:

    • 获取 store 存储的内容(具体咱们能够通过 useStore(selector, enqulity) 第一个参数获取 store 切片)
    • 通过 useEffect or useLayoutEffect 初始化组件 store 值,并且将 state 变动事件注入 listeners;

下图是我总结出其 zustand 状态治理的整个流程,大家能够参考一下:

3、手写一个 toy 级别的 zustand

  下面咱们剖析了 zustand 执行的过程及状态治理的流程,上面咱们就尝试着手写toyzustand,这块咱们分成两块,一个是创立 store 局部,一个是创立 useStore hooks 的局部,具体如下:

function createStore(createState) {
    let state;
    let listeners = new Set();
    // 获取 store 内容
    const getState = () => state;
    // 更新 store 内容
    const setState = (partial, replace) => {const nextState = typeof partial === 'function' ? partial(state) : partial;

        if (nextState !== state) {
            const prevState = state;
            state = replace ? nextState : Object.assign({}, state, nextState);
            listeners.forEach(listener => listener(state, prevState));
        }
    }
    // 增加订阅信息
    const subscribe = (listener) => {listeners.add(listener);
        // 革除订阅信息
        return () => { listeners.delete(listener); };
    }
    // 革除所有的 listener
    const destroy = () => listeners.clear();
    
    const api = {getState, setState, destroy, subscribe};
    // 创立初始的 state
    state = createState(setState, getState, api);

    return api;
}

export default createStore

生成 hooks 办法

import {useLayoutEffect} from "react";
import {useReducer, useRef} from "react";
import createStore from "./createStore";

function create(createState) {
    // 依据 createStore 联合 createState 创立一个 store
    const api = createStore(createState);

    /**
     * @description 创立 hooks
     * @param {Function} selector  可选的,返回 store 的内容,默认 api.getState
     * @param {Function} enqulityFn  可选,默认用 Object.is 判断
     * @returns 
     */
    const useStore = (selector = api.getState, enqulityFn = Object.is) => {
        // 生辰一个 forceUpdate 函数
        const [, forceUpdate] = useReducer(c => c + 1, 0);

        const state = api.getState();
        const stateRef = useRef(state);
        // 存储办法
        const selectorRef = useRef(selector);
        const enqulityFnRef = useRef(enqulityFn);

        // 以后 current 状态存储
        let currentStateRef = useRef();
        if (currentStateRef.current === undefined) {currentStateRef.current = selector(state);
        }

        /**
         * 以后用户所须要的状态切片(这块须要留神,zustand 用户能够依据 selector 获取局部 store 内容值)* 所以咱们判断是否须要更新,比照的是切片内容,而非整个 store
         */
        let newStateSlice;
        // 更新标记
        let hasNewStateSlice = false;

        if (stateRef.current !== state || selector !== selectorRef.current || enqulityFn !== enqulityFnRef.current) {newStateSlice = selector(state);
            hasNewStateSlice = !enqulityFn(newStateSlice, currentStateRef.current);
        }

        // 初始化数据
        useLayoutEffect(() => {if (hasNewStateSlice) {currentStateRef.current = newStateSlice;}
            stateRef.current = state;
            selectorRef.current = selector;
            enqulityFnRef.current = enqulityFn;
        })

        // 增加 state 变动订阅事件
        useLayoutEffect(() => {const listener = () => {
                // 获取以后最新的 state 状态值
                const nextState = api.getState();
                // 拿到以后用户所需的 store 切片
                const nextStateSlice = selectorRef.current(nextState);
                // 比拟以后用户 current 切片 与 最新 store 切片是否是一样的,如果不一样,就更新到最新的切片
                if (!enqulityFnRef.current(nextStateSlice, currentStateRef.current)) {
                    stateRef.current = nextState;
                    currentStateRef.current = nextStateSlice;
                    forceUpdate();}
            }
            const unSubscribe = api.subscribe(listener);
            // 当组件销毁,咱们须要勾销订阅
            return unSubscribe
        }, []);

        // 返回用户所需切片
        const sliceToReturn = hasNewStateSlice ? newStateSlice: currentStateRef.current;

        return sliceToReturn;

    }
    // 将批改 store 的办法 {getState, setState, destroy, subscribe} 裸露进来,这样用户能够脱离 react 组件去应用状态治理
    // example: useStore.getState() ....
    Object.assign(useStore, api);

    return useStore;
}

export default create

我的项目中应用办法:

// 创立 store
import create from '../create'

export const useCounterStore = create(set => ({
  count: 0,
  increament: () => set(state => ({ count: state.count + 1})),
}))

// 我的项目中应用形式
import React from 'react';
import {useCounterStore} from './store';

const Other = () => {const counter = useCounterStore();
    return (
        <div>
            <h1>Other</h1>
            <div>
                <div>{counter.count}</div>
            </div>
        </div>
    );
}

export default Other;

本期先到这儿,如果感觉还不错,就点赞加关注反对一下~

退出移动版