Zustand 狀態管理實戰:為什麼我們在 PWA 中棄用 Redux 轉向更輕量的 Zustand?
探討 DueWise 如何利用 Zustand 簡化複雜的物品效期狀態管理,並分析其在 PWA 環境下的效能優勢。
Zustand 狀態管理實戰:為什麼我們在 PWA 中棄用 Redux 轉向更輕量的 Zustand?
在開發 React 應用程式時,狀態管理始終是一個繞不開的話題。早期的專案大多傾向於使用成熟的 Redux,但在 DueWise 這樣一個講求輕量、極速、且需要頻繁同步雲端數據的 PWA 專案中,Redux 的繁瑣模版程式(Boilerplate)反而成了開發的阻礙。最終我們選擇了 Zustand。
這篇文章將揭示我們為何做出這個轉變,以及在實作中的具體心得。
為什麼 Redux 不再是首選?
Redux 的強大無庸置疑,特別是它的開發者工具與強大的生態系。但在 DueWise 中,我們的狀態主要集中在三個維度:
- 使用者設定 (Settings): 包含語系、主題、閾值設定等。
- 物品清單 (Items): 來自於 IndexedDB 或 D1 的資料。
- UI 狀態: 掃描視窗開啟、載入動畫等。
如果使用 Redux,為了實作一個簡單的「新增物品」功能,我們需要定義 ActionType、撰寫 Action Creator、更新 Reducer、並在組件中使用 useDispatch。這對於注重開發速度的小型團隊來說,實在過於沉重。
Zustand 的簡約之美
Zustand (德文中的「狀態」) 走的是一條截然不同的路。它基於 Hook,沒有繁雜的概念。
1. 定義即使用
在 src/store/useAppStore.ts 中,我們定義狀態的方式幾乎與定義一般的 JavaScript 物件無異:
import { create } from 'zustand';
interface AppState {
items: Item[];
isScannerOpen: boolean;
setItems: (items: Item[]) => void;
toggleScanner: () => void;
}
export const useAppStore = create<AppState>((set) => ({
items: [],
isScannerOpen: false,
setItems: (items) => set({ items }),
toggleScanner: () => set((state) => ({ isScannerOpen: !state.isScannerOpen })),
}));
2. 精準的組件重繪 (Re-renders)
Zustand 的一個核心優勢是「選擇器 (Selectors)」。這讓 React 組件只會在它真正需要的狀態變更時才重新渲染。
// 只有當 items 數量變動時,此組件才會重繪
const itemCount = useAppStore((state) => state.items.length);
這在處理 DueWise 的統計圖表(Stats View)時非常有幫助,因為圖表只需要追蹤 items 的整體數據,而不需要關心使用者是否打開了某個選項視窗。
在 PWA 環境下的獨特優勢
1. 輕量化的 Bundle Size
PWA 的加載速度與 JS Bundle 的大小息息相關。Zustand 的體積僅約 1KB,相較於 Redux + React-Redux + Redux-Thunk,這能有效減少初始加載的時間。
2. 與非 React 代碼的整合
這是我們最喜歡的一點。由於 DueWise 在 Service Worker 或一些特定的 Utility 函數中也需要讀取狀態,Zustand 提供了 getState() 與 setState(),讓你在 React 組件之外也能輕鬆操作數據,不需要經過繁瑣的 Context。
結合持久化 (Persistence)
由於 DueWise 支援「在地模式 (Guest Mode)」,我們需要將用戶的本地數據持久化。Zustand 內建的 persist 中間件簡直是救星:
import { persist, createJSONStorage } from 'zustand/middleware';
export const useSettingsStore = create(
persist(
(set) => ({
theme: 'dark',
setTheme: (theme) => set({ theme }),
}),
{
name: 'duewise-settings',
storage: createJSONStorage(() => localStorage),
}
)
);
這樣一來,使用者所有的偏好設定在重新開啟 PWA 時都會被自動恢復,完全不需要手動寫 localStorage.getItem 的邏輯。
個人心得:開發體驗的回歸
在 DueWise 的開發過程中,轉向 Zustand 讓我重新體驗到了「寫程式應該是快樂的」這件事。我們不再需要花 20 分鐘去配置一個狀態流,而是可以在 30 秒內定義好一個全域狀態並在多個組件間共享。
對於 90% 的現代應用程式來說,Redux 都顯得過於笨重。如果你在開發一個講求速度與簡約的 PWA,Zustand 絕對是你最好的夥伴。
結論
狀態管理不應該成為專案的負擔。透過 Zustand,我們成功地減少了 DueWise 的代碼複雜度,並提升了執行效能。
下一篇文章,我們將轉向通訊技術的核心:「Web Push Notifications 全解析:從 VAPID 金鑰到動態提醒實戰」。