在 Zustand 中實(shí)現(xiàn) computed 的方式
引言
在狀態(tài)管理領(lǐng)域,計(jì)算屬性(computed properties)是一個(gè)極其重要的概念。MobX 和 Pinia 等庫都內(nèi)置了計(jì)算屬性功能,允許開發(fā)者聲明式地定義派生狀態(tài)。雖然 Zustand 本身沒有直接提供 computed API,但這并不意味著我們無法實(shí)現(xiàn)類似的功能。
本文將介紹三種在 Zustand 中實(shí)現(xiàn)計(jì)算屬性的優(yōu)雅方式,包含官方推薦等方案。
方案一:derive-zustand
https://github.com/zustandjs/derive-zustand 是 Zustand 官方維護(hù)的派生狀態(tài)庫,它提供了一種響應(yīng)式的方式來處理計(jì)算邏輯。
核心優(yōu)勢
- 響應(yīng)式更新:自動(dòng)追蹤依賴,當(dāng)依賴狀態(tài)變化時(shí)自動(dòng)更新
- 類型安全:完美支持 TypeScript 類型推斷
- 性能優(yōu)化:避免不必要的重新計(jì)算
基本用法
import { create, useStore } from 'zustand';
import { derive } from 'derive-zustand';
// 基礎(chǔ) store
const useCountStore = create<{ count: number; inc: () => void }>((set) => ({
count: 0,
inc: () => set((state) => ({ count: state.count + 1 })),
}));
// 派生 store
const doubleCountStore = derive<number>((get) => get(useCountStore).count * 2);
// 自定義 hook
const useDoubleCountStore = () => useStore(doubleCountStore);
// 組件中使用
const Counter = () => {
const { count, inc } = useCountStore();
const doubleCount = useDoubleCountStore();
return (
<div>
<div>count: {count}</div>
<div>doubleCount: {doubleCount}</div>
<button type="button" onClick={inc}>
+1
</button>
</div>
);
};
適用場景
- 需要多個(gè)組件共享的派生狀態(tài)
- 復(fù)雜的計(jì)算邏輯需要復(fù)用
- 希望保持響應(yīng)式更新的特性
方案二:手動(dòng)維護(hù)計(jì)算屬性
對于簡單的計(jì)算需求,可以直接在 store 中聲明派生狀態(tài),并在相關(guān)操作后手動(dòng)更新。
實(shí)現(xiàn)模式
import { create } from 'zustand';
type CartItem = { id: string; price: number; quantity: number };
type CartState = {
items: CartItem[];
total: number; // ← 計(jì)算屬性
addItem: (item: Omit<CartItem, 'id'>) => void;
updateTotal: () => void;
};
const useCartStore = create<CartState>((set, get) => ({
items: [],
total: 0,
addItem: (item) => {
set((state) => ({
items: [...state.items, { ...item, id: crypto.randomUUID() }],
}));
get().updateTotal(); // 添加商品后更新總價(jià)
},
updateTotal: () => {
const newTotal = get().items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
set({ total: newTotal });
},
}));
優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 不需要額外依賴
- 邏輯集中,便于維護(hù)
- 更新時(shí)機(jī)明確可控
缺點(diǎn):
- 需要手動(dòng)觸發(fā)更新
- 可能遺漏更新點(diǎn)
- 不適合復(fù)雜依賴關(guān)系
最佳實(shí)踐
- 為計(jì)算屬性添加專門的更新方法
- 在文檔中明確標(biāo)注哪些操作會影響計(jì)算屬性
- 考慮使用
immer簡化不可變更新邏輯
方案三:在組件內(nèi)派生狀態(tài)
對于簡單的、僅限單個(gè)組件使用的派生狀態(tài),可以直接在組件內(nèi)部計(jì)算。
實(shí)現(xiàn)示例
const UserProfile = () => {
const firstName = useUserStore((s) => s.firstName);
const lastName = useUserStore((s) => s.lastName);
const fullName = `${firstName} ${lastName}`;
return (
<div>{fullName}</div>
);
};
適用條件
- 派生狀態(tài)只在一個(gè)組件中使用
- 計(jì)算邏輯非常簡單
- 不需要響應(yīng)式更新(或可以接受組件重新渲染)
性能考慮
當(dāng)派生計(jì)算較復(fù)雜時(shí),可以使用 useMemo 優(yōu)化:
const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
方案對比與選擇指南
| 方案 | 適用場景 | 復(fù)雜度 | 性能 | 維護(hù)性 |
|---|---|---|---|---|
| derive-zustand | 多組件共享的復(fù)雜派生狀態(tài) | 中 | 優(yōu) | 優(yōu) |
| Store 內(nèi)維護(hù) | 簡單的全局計(jì)算屬性 | 低 | 良 | 中 |
| 組件內(nèi)計(jì)算 | 單一組件使用的簡單派生 | 最低 | 視情況 | 視情況 |
選擇建議:
- 優(yōu)先考慮 derive-zustand,特別是需要響應(yīng)式更新時(shí)
- 對于簡單場景,Store 內(nèi)維護(hù)更輕量
- 組件內(nèi)計(jì)算適合臨時(shí)性、局部性的簡單邏輯
高級技巧:組合使用多種方案
在實(shí)際項(xiàng)目中,你可以靈活組合這些方案。例如:
// 使用 derive-zustand 創(chuàng)建基礎(chǔ)派生狀態(tài)
const filteredTodosStore = derive<Todo[]>(get => {
const { todos, filter } = get(useTodoStore);
return todos.filter(todo =>
filter === 'all' ||
(filter === 'completed' && todo.completed) ||
(filter === 'active' && !todo.completed)
);
});
// 在組件內(nèi)進(jìn)一步派生
const TodoStats = () => {
const filteredTodos = useStore(filteredTodosStore);
// 組件特有的派生狀態(tài)
const completionPercentage = useMemo(() => {
if (filteredTodos.length === 0) return 0;
const completed = filteredTodos.filter(t => t.completed).length;
return Math.round((completed / filteredTodos.length) * 100);
}, [filteredTodos]);
return <div>完成度: {completionPercentage}%</div>;
};
總結(jié)與最佳實(shí)踐
在 Zustand 中實(shí)現(xiàn)計(jì)算屬性有多種方式,沒有絕對的"最佳"方案,關(guān)鍵是根據(jù)具體場景選擇最合適的:
- 保持簡單:不要過度設(shè)計(jì),簡單的組件內(nèi)計(jì)算可能就足夠了
- 關(guān)注性能:對于昂貴的計(jì)算,使用 memoization 技術(shù)
- 類型安全:充分利用 TypeScript 確保類型正確
- 文檔說明:明確標(biāo)注哪些是計(jì)算屬性及其依賴關(guān)系
- 測試覆蓋:為重要的計(jì)算邏輯添加單元測試
Zustand 的靈活性允許你根據(jù)項(xiàng)目需求選擇最適合的計(jì)算屬性實(shí)現(xiàn)方式,這種設(shè)計(jì)哲學(xué)正是它受到開發(fā)者喜愛的原因之一。

浙公網(wǎng)安備 33010602011771號