- 作者:老汪软件技巧
- 发表时间:2024-12-29 21:03
- 浏览量:
Zustand 是什么?
Zustand 是一个轻量级、易于使用的状态管理库,专为 React 应用设计。它的名字来源于德语,意为“状态”(state)。Zustand 的核心理念是“简单即美”,它通过提供简洁的 API 和灵活的使用方式,帮助开发者高效地管理应用状态,而不必陷入复杂的配置和冗长的代码中。
Github 地址:Zustand
一、Zustand的使用
从概念上Zustand与Redux类似,都是基于不可变的 state 模型。但是,Redux 要求程序包装在上下文提供程序中;Zustand 并没有要求你使用上下文提供程序来包裹整个应用。相反,它提供了更细粒度的控制,让你可以在需要的地方创建和使用状态存储。
创建Store
import { create } from "../zustand-ky";
// import { create } from 'zustand';
interface BearState {
bears: number;
increase: (count?: number) => void;
decrease: (count?: number) => void;
reset: () => void;
}
const useBearStore = create<BearState>((set, get) => ({
bears: 0,
increase: (count = 1) => set(state => ({ bears: state.bears + count })),
decrease: (count = 1) => set(state => ({ bears: Math.max(0, state.bears - count) })),
reset: () => set({ bears: 0 }),
}));
export default useBearStore;
在组件里调用store里的状态与方法
import { memo } from 'react';
import useBearStore from '../store/useBearStore';
const Bears = () => {
const bearsStore = useBearStore();
const { bears, increase, decrease, reset } = bearsStore;
return (
<div>
<h3>BearsPageh3>
<p>Number of bears: {bears}p>
<button onClick={() => increase()}>Increasebutton>
<button onClick={() => decrease()}>Decreasebutton>
<button onClick={() => reset()}>Resetbutton>
<Child />
div>
);
};
const Child = memo(() => {
// const bears = useBearStore(state => state.bears);
const { bears } = useBearStore();
return (
<div>
<h3>ChildPageh3>
<p>{bears}p>
div>
);
});
用户更改状态
获取所有内容
可以获取到所有的状态,但是这样做会导致组件在每次状态变化时都进行重新渲染。
const state = useBearStore();
选择状态片段
通过传入selecor进行单个或者多个状态选择。默认情况下,Zustand 使用Object.is来检测变化。
const bears = useBearStore((state) => state.bears);
当构造多个状态对象时,由于每次返回的都是新对象,会导致不必要的渲染。因此可以传入Zustand提供的useShallow来防止当选择器输出根据浅相等不变时不必要的重新渲染。
import { create } from "zustand";
import { useShallow } from "zustand/react/shallow";
const { bears, increase } = useBearStore(
useShallow((state) => ({ bears: state.bears, increase: state.increase })),
);
覆盖状态
set 函数有第二个参数,默认为 false。当设置为true时,它将替换状态模型。
const useBearStore = create<BearState>((set, get) => ({
bears: 0,
increase: (count = 1) => set(state => ({ bears: state.bears + count })),
decrease: (count = 1) => set(state => ({ bears: Math.max(0, state.bears - count) })),
reset: () => set({},true), //替换整个state,不是合并
}));
二、Zustand的实现
从上面的使用案例中可以看到,Zustand 的工作方式与 Redux 有很多相似之处。例如,状态值不能直接修改,都是通过 setState 方法来触发修改,保证了状态的不可变性,使状态管理更加可预测和易于调试。此外,Zustand 在通知状态变化时使用了发布订阅模式,只有订阅了相应状态的组件才会被更新,从而提高了应用的性能。
任何状态管理工具的核心都围绕着两个基本要素:
单一的状态容器(简称:store):这个容器负责持有应用的状态或数据,并提供接口让外部代码可以对这些状态进行查询、添加、删除和修改。观察者模式:当store中的状态发生变更时,它会通过事件通知机制告知所有监听该状态的组件或模块,使它们能够获取并响应最新的状态变化。
Zustand 也不例外,它通过一个单一的 store 来集中管理状态,并利用观察者模式确保状态的变化能够被及时且高效地传播给所有相关的监听者。
我们将分两部分来实现简易版的 Zustand:index.ts 和 vanilla.ts。index.ts 负责提供高层的 API,包括与React的交互,订阅状态以及暴露给开发者的API,而 vanilla.ts 则实现了状态管理的核心逻辑(完整代码将会在文末给出)。
2.1 index.ts文件解析
index.ts 文件主要负责定义和导出高层次的 API,其中最主要的则是 create 函数和 useStore Hook。
create函数
create函数是Zustand暴露给开发者的 API,它接受一个 createStateFn 函数作为参数,并返回一个自定义的 useBoundStore hook。这个 hook 允许组件订阅状态,并获取最新的状态。
之所以将入参设置为函数,是为了更好的支持中间件的开发,中间件只需要接收之间的函数,并返回一个新的函数就可以达到中间处理的效果,不需要改动Zustand的源码。
可以发现这个函数的内容非常简单,总共只有三行代码,而创造store的核心则是第一行的createStore方法。
//实现create函数,接受一个函数作为参数,函数的作用是创建仓库对象
export const create: Creator = (createStateFn: creatorState ) =>
createStateImpl(createStateFn);
const createStateImpl = (createStateFn: creatorState ) => {
// 调用创建store方法
const api = createStore(createStateFn);
//创建一个自定义hook,供组件使用并返回
const useBoundStore: any = (selector?: any) => useStore(api, selector);
Object.assign(useBoundStore, api);
return useBoundStore;
};
useStore Hook
useStore 负责与React组件进行连接,用于在 React 组件中订阅状态变化并获取最新状态。它使用了 useSyncExternalStore 来确保状态变化时组件能够及时更新。因此使用 zustand 不需要给程序包裹 context provider。
identity 函数是一个非常简单的通用函数,它接收一个参数并返回相同的参数,同时保留其类型信息。在 useStore 函数中,identity 作为默认的选择器函数,当没有提供选择器时,它会直接返回整个状态。
//默认返回自身的函数
const identity = (arg: T): T => arg;
// 函数重载
export function useStoreextends ReadonlyStoreApi<unknown>>(api: S): GetState;
export function useStoreextends ReadonlyStoreApi<unknown>, U>(
api: S,
selector: (state: GetState) => U
): U;
export function useStore<TState, StateSlice>(
api: ReadonlyStoreApi<TState>,
selector: (state: TState) => StateSlice = identity as any
) {
const slice = useSyncExternalStore(
api.subscribe,
() => selector(api.getState()),
() => selector(api.getInitialState())
);
return slice;
}
useSyncExternalStore
useSyncExternalStore是React 提供的一个 Hook,用于同步外部状态。它接收三个参数:subscribe 、 getState 和getServerSnapshot,分别用于订阅状态变化和获取当前状态。
具体用法可查看React官方文档(useSyncExternalStore用法)[]