对于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;
- 获取store存储的内容(具体咱们能够通过
下图是我总结出其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
我的项目中应用办法:
// 创立storeimport 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;
本期先到这儿,如果感觉还不错,就点赞加关注反对一下~