Skip to content
Anonymous

Zustand 狀態管理實戰:為什麼我們在 PWA 中棄用 Redux 轉向更輕量的 Zustand?

探討 DueWise 如何利用 Zustand 簡化複雜的物品效期狀態管理,並分析其在 PWA 環境下的效能優勢。

#Zustand #React #State Management #Performance

Zustand 狀態管理實戰:為什麼我們在 PWA 中棄用 Redux 轉向更輕量的 Zustand?

在開發 React 應用程式時,狀態管理始終是一個繞不開的話題。早期的專案大多傾向於使用成熟的 Redux,但在 DueWise 這樣一個講求輕量、極速、且需要頻繁同步雲端數據的 PWA 專案中,Redux 的繁瑣模版程式(Boilerplate)反而成了開發的阻礙。最終我們選擇了 Zustand

這篇文章將揭示我們為何做出這個轉變,以及在實作中的具體心得。

為什麼 Redux 不再是首選?

Redux 的強大無庸置疑,特別是它的開發者工具與強大的生態系。但在 DueWise 中,我們的狀態主要集中在三個維度:

  1. 使用者設定 (Settings): 包含語系、主題、閾值設定等。
  2. 物品清單 (Items): 來自於 IndexedDB 或 D1 的資料。
  3. 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 金鑰到動態提醒實戰」。