Zustand
基本使用
@/stores/cnt.ts
@/stores/cnt.ts
import { create } from "zustand";
interface ICntStore {
cnt: number;
incCnt: () => void;
decCnt: () => void;
resetCnt: () => void;
getCnt: () => number;
}
const useCntStore = create<ICntStore>((set, get) => {
return {
cnt: 0,
incCnt: () => set((state) => ({ cnt: state.cnt + 1 })),
decCnt: () => set((state) => ({ cnt: state.cnt - 1 })),
resetCnt: () => set({ cnt: 0 }),
getCnt: () => get().cnt,
};
});
export default useCntStore;深层次状态
immer
import { produce } from "immer";
const data = { user: { name: "lark", age: 22 } };
const newData = produce(data, (draft) => {
draft.user.age = 23;
});
// {user: {name: 'lark', age: 23}}, false
console.log(newData, newData === data);zustand 使用 immer 中间件
不使用 immer 中间件
不使用 immer 中间件
import { create } from "zustand";
interface IGroup {
cnt: {
female: number;
male: number;
};
addMale: () => void;
}
const useGroupStore = create<IGroup>((set) => ({
cnt: {
female: 0,
male: 1,
},
addMale: () =>
set((state) => ({
cnt: {
...state.cnt,
male: state.cnt.male + 1,
},
})),
}));
export default useGroupStore;immer 原理: Proxy 代理
const obj = {
user: {
name: "lark",
age: 23,
},
};
const isObject = (val) => typeof val === "object" && val !== null;
// recipe: (draft) => void
const produce = (base, recipe) => {
const modified = {};
const /** @type {ProxyHandler} */ handler = {
get(target, prop, receiver) {
if (prop in modified) {
return modified[prop];
}
if (isObject(target[prop])) {
return new Proxy(target[prop], handler);
}
return Reflect.get(target, prop, receiver);
},
set(target, prop, value) {
return Reflect.set(modified, prop, value);
},
};
const draft = new Proxy(base, handler);
recipe(draft);
if (Object.keys(modified).length === 0) {
return base;
}
return draft;
};
const newObj = produce(obj, (draft) => {
draft.user.name = "lark2";
draft.user.age++;
});
console.log(newObj, obj);状态切片, useShallow
- 使用解构: setSing 时, 会导致
<First />组件更新; setDance 时, 也会导致<First />组件更新 - 使用状态切片: setSing 时, 会导致
<Second />组件更新; setDance 时, 不会导致<Second />组件更新 - 使用 useShallow 中间件: setSing 时, 会导致
<Third />组件更新; setDance 时, 不会导致<Third />组件更新
@/stores/kun.ts
@/stores/kun.ts
import { create } from "zustand";
interface IKun {
name: string;
hobbies: {
sing: string;
dance: string;
};
setSing: (newSing: string) => void;
setDance: (newDance: string) => void;
}
const useKunStore = create<IKun>((set) => ({
name: "kun",
hobbies: {
sing: "sing",
dance: "dance",
},
setSing: (newSing: string) => {
set((state) => ({
...state,
hobbies: { ...state.hobbies, sing: newSing },
}));
},
setDance: (newDance: string) => {
set((state) => ({
...state,
hobbies: { ...state.hobbies, dance: newDance },
}));
},
}));
export default useKunStore;中间件
- immer 中间件
- devtools 调试中间件
- persist 持久化中间件
@/stores/kun.ts
@/stores/kun.ts
import { create } from "zustand";
interface IKun {
name: string;
hobbies: {
sing: string;
dance: string;
};
setSing: (newSing: string) => void;
setDance: (newDance: string) => void;
}
const useKunStore = create<IKun>((set) => ({
name: "kun",
hobbies: {
sing: "sing",
dance: "dance",
},
setSing: (newSing: string) => {
set((state) => ({
...state,
hobbies: { ...state.hobbies, sing: newSing },
}));
},
setDance: (newDance: string) => {
set((state) => ({
...state,
hobbies: { ...state.hobbies, dance: newDance },
}));
},
}));
export default useKunStore;自定义 logger 中间件
@/middlewares/logger.js
@/middlewares/logger.js
export const logger = (fn) => (set, get, storeApi) => {
const decoratedSet = (...args) => {
console.log("[set] before", get());
set(...args);
console.log("[set] after", get());
};
return fn(decoratedSet, get, storeApi);
};subscribe
subscribe 订阅
useStore.subscribe((state) => console.log(state) /** listener */);subscribe 订阅: state 的任意属性改变时, 都会触发 listener 的调用
- 组件外部订阅
- 组件内部订阅: 需要写在 useEffect 中, 并且依赖项数组 deps 是 [] 空数组, 只会在组件挂载后订阅一次, 防止重复订阅
示例
- 未使用 subscribe 订阅, 每次 age 改变时, 都会触发组件更新
- 使用 subscribe 订阅, age 改变时, 不会触发组件更新; 只有 isYoung 状态改变时, 才会触发组件更新
@/stores/user.ts
@/stores/user.ts
import { create } from "zustand";
interface IUser {
name: string;
age: number;
setName: (newName: string) => void;
incAge: () => void;
decAge: () => void;
}
const useUserStore = create<IUser>((set) => ({
name: "lark",
age: 18,
setName: (newName: string) => set(() => ({ name: newName })),
incAge: () => set((state) => ({ age: state.age + 1 })),
decAge: () => set((state) => ({ age: state.age - 1 })),
}));
export default useUserStore;subscribe + subscribeWithSelector 中间件
subscribe + subscribeWithSelector 中间件: state 的指定属性改变时, 才会触发 listener 的调用
示例
- 未使用 subscribeWithSelector 中间件: state 的任意属性改变时, 都会触发 listener 的调用
- 使用 subscribeWithSelector 中间件: 仅 state 的 age 属性改变时, 才会触发 listener 的调用
@/stores/user.ts
@/stores/user.ts
import { create } from "zustand";
import { subscribeWithSelector } from "zustand/middleware";
interface IUser {
name: string;
age: number;
setName: (newName: string) => void;
incAge: () => void;
decAge: () => void;
}
const useUserStore = create<IUser>()(
subscribeWithSelector((set) => ({
name: "lark",
age: 18,
setName: (newName: string) => set(() => ({ name: newName })),
incAge: () => set((state) => ({ age: state.age + 1 })),
decAge: () => set((state) => ({ age: state.age - 1 })),
})),
);
export default useUserStore;Last updated on