Zustand 輕量級狀態管理教學 分析 Slice Pattern 與 Redux 差異之高品質選型策略

me
林彥成
2024-10-02 | 5 min.
文章目錄
  1. 1. 什麼是 Zustand?
  2. 2. 為什麼不該再繼續用 Redux 了?
  3. 3. Redux 的局限性
  4. 4. Zustand 的特點與優勢
    1. 4.1. 簡單易用的文件
    2. 4.2. 無需轉換 DevTool
    3. 4.3. 快速入門
  5. 5. Zustand 設計模式: Slice Pattern
  6. 6. Zustand X 個人職涯發展
  7. 7. Zustand 對專案的優化
    1. 7.1. 支援 Redux Devtool
    2. 7.2. 支援狀態持久化到瀏覽器的儲存
    3. 7.3. 支援非同步
    4. 7.4. 支援 immer 進行多層的物件操作
    5. 7.5. 架構面: 提供在元件外操作共用的狀態
  8. 8. FAQ:Zustand 狀態管理常見問題
    1. 8.1. Q1:Zustand 與 Redux 最大的區別是什麼?
    2. 8.2. Q2:什麼時候該選擇 Zustand 而不是 React Context?
    3. 8.3. Q3:Zustand 適合大型專案嗎?

什麼是 Zustand?

Zustand 是一款基於 Flux 架構的輕量級 React 狀態管理 工具,以其極簡的 API、無需 Provider 包裹以及優異的效能而聞名。它使用 Hooks 作為主要互動方式,解決了傳統 Redux 樣板程式碼(Boilerplate)過多的問題,同時支援非同步操作、中間件(Middleware)與 DevTools,是現代前端開發中替代 Redux 的熱門首選方案。


在現代前端開發中,選擇合適的 React 狀態管理 方案至關重要。當我選擇 Zustand 作為狀態集中管理的解決方案時,有很多原因促使我做出這個決定。Zustand 的輕量級和彈性特性使其在架構上無縫穿梭於專案的各個角落,避免了因架構限制而導致的可讀性下降和優化難度增加。

以下是我使用 Zustand 快兩年後的一些心得,透過這份 Zustand 教學,我將從幾個層面來深入分析其優勢。

為什麼不該再繼續用 Redux 了?

選擇狀態集中管理的解決方案時,Zustand 的輕量級和彈性特性讓我不再依賴 Redux。儘管 Redux 曾經為 FLUX 單向資料流提供接近典範的解決方案,但隨著時間推移,它逐漸暴露出以下問題:

Redux 的局限性

  • 樣板程式碼繁多:Redux 需要大量的樣板程式碼,包括 actions、reducers 和 store 的設置,這增加了開發的複雜性。
  • 過度抽象:對於剛進入前端領域的開發者來說,Redux 的抽象層可能使簡單的狀態管理變得複雜,這對於小型或中型應用來說顯得多餘。而且,理解 Redux 的工作原理(如中間件和非同步處理)需要時間,可能影響團隊的開發效率。
  • 效能問題: 在狀態更新過程中,Redux 可能導致不必要的重新渲染,這在大型應用中會成為性能瓶頸。

隨著 React 的演進,如 Hooks 的加入,Redux 的生態系統顯得越來越過時,儘管 Redux Toolkit 嘗試解決這些問題,但歷史的包袱仍然存在。因此,我開始探索更好的選擇。

Zustand 的特點與優勢

在使用 Zustand 快兩年後,我從以下幾個層面進行了分析:

簡單易用的文件

官方文件清晰可讀,並具有可愛的設計和滾動視差效果。這不僅提高了開發者的學習效率,也使得查找資訊變得更加直觀。此外,Zustand 的社群也提供了大量的範例和教程,進一步降低了入門的難度。

無需轉換 DevTool

從 Redux 轉移過來時,原有的 DevTool 可以直接使用,降低了學習成本。這意謂著開發者可以快速上手,無需重複學習工具的使用方法,從而提高開發效率。

快速入門

QuickStart 實現迅速,讓開發者能夠迅速上手,Zustand 使用 hooks 來管理狀態,這與 React 的設計理念更加契合,使得狀態的使用變得更直觀。

官網簡潔易懂: https://zustand-demo.pmnd.rs/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { create } from "zustand";

const useStore = create((set) => ({
count: 1,
inc: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
const { count, inc } = useStore();
return (
<div>
<span>{count}</span>
<button onClick={inc}>one up</button>
</div>
);
}

Zustand 設計模式: Slice Pattern

Zustand 提供的 Slice Pattern 使狀態管理更加模組化,便於組織和擴展。

這種設計模式讓開發者可以將狀態劃分為不同的「切片」,每個切片可以獨立管理,這樣不僅提高了可讀性,還促進了團隊的協作開發,因為不同的開發者可以同時在不同的切片上工作,降低了衝突的風險。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const createFishSlice = (set) => ({
fishes: 0,
addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
});

const createBearSlice = (set) => ({
bears: 0,
addBear: () => set((state) => ({ bears: state.bears + 1 })),
eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),
});

import { create } from "zustand";
import { createBearSlice } from "./bearSlice";
import { createFishSlice } from "./fishSlice";

export const useBoundStore = create((...a) => ({
...createBearSlice(...a),
...createFishSlice(...a),
}));

Zustand X 個人職涯發展

每次換新工作時,理論上都應該學會一次新的解決方案。

我記得最早使用 Zustand 時,因為專案時間緊迫,但我的老闆給了我很大的自由度,當時也是新專案而且時程有被報告追趕的壓力,於是投資了一隻看起來很可愛的熊當狀態管理的工具。

當然,我認為要選就要選可愛的?!

Zustand 對專案的優化

Zustand 的設計使其易於與其他函式庫(如 React Query)整合,並且提供 middleware,使需求的實現變得簡單。我建議維持 Single Source of Truth 的原則,以避免狀態不一致的問題。

Zustand 的進階寫法支援狀態持久化和多層的狀態更新,這些特性使開發變得更加方便。

接下來,熊熊稱霸世界?!

支援 Redux Devtool

1
2
3
4
5
6
import { devtools } from 'zustand/middleware'

// Usage with a plain action store, it will log actions as "setState"
const usePlainStore = create(devtools((set) => ...))
// Usage with a redux store, it will log full action types
const useReduxStore = create(devtools(redux(reducer, initialState)))

支援狀態持久化到瀏覽器的儲存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";

const useFishStore = create(
persist(
(set, get) => ({
fishes: 0,
addAFish: () => set({ fishes: get().fishes + 1 }),
}),
{
name: "food-storage", // name of the item in the storage (must be unique)
storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used
}
)
);

支援非同步

Zustand 支援非同步操作,開發者可以在 store 裡面實作 fetch 及 setState() 的邏輯。

1
2
3
4
5
6
7
const useFishStore = create((set) => ({
fishies: {},
fetch: async (pond) => {
const response = await fetch(pond);
set({ fishies: await response.json() });
},
}));

支援 immer 進行多層的物件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";

const useBeeStore = create(
immer((set) => ({
bees: 0,
addBees: (by) =>
set((state) => {
state.bees += by;
}),
}))
);

// --------------------

import { produce } from "immer";

const useLushStore = create((set) => ({
lush: { forest: { contains: { a: "bear" } } },
clearForest: () =>
set(
produce((state) => {
state.lush.forest.contains = null;
})
),
}));

const clearForest = useLushStore((state) => state.clearForest);
clearForest();

架構面: 提供在元件外操作共用的狀態

Zustand 允許開發者在元件外直接操作共用狀態,提高了靈活性。

底下是官網的範例可以透過已經持久化的 store 本身直接進行 getState() 或是 setState() 的狀態操作。

1
2
3
4
5
6
7
8
const useDogStore = create(() => ({ paw: true, snout: true, fur: true }));

// Getting non-reactive fresh state
const paw = useDogStore.getState().paw;
// Listening to all changes, fires synchronously on every change
const unsub1 = useDogStore.subscribe(console.log);
// Updating state, will trigger listeners
useDogStore.setState({ paw: false });

因為這樣的特性,Zustand 也提供了 vanillajs 的版本,便於在多種使用場景下使用。

1
2
3
4
5
6
import { createStore } from 'zustand/vanilla'

const store = createStore((set) => ...)
const { getState, setState, subscribe, getInitialState } = store

export default store

FAQ:Zustand 狀態管理常見問題

Q1:Zustand 與 Redux 最大的區別是什麼?

A:Zustand 不需要 Provider 包裹整個應用,這意味著它不會在狀態更新時導致整棵元件樹的 Context 重新計算。此外,Zustand 的程式碼量通常只有 Redux 的 1/10。

Q2:什麼時候該選擇 Zustand 而不是 React Context?

A:當您的狀態更新頻率較高,或者狀態邏輯跨越大量元件時,Zustand 的訂閱機制(Subscription)能精確只重新渲染使用該狀態的元件,效能優於會導致全域重新渲染的 Context。

Q3:Zustand 適合大型專案嗎?

A:絕對適合。透過 Slice Pattern 與其對 TypeScript 的優異支援,Zustand 在管理複雜的大型應用狀態時比 Redux 更加清晰且易於擴展。

在新的狀態管理方案中,Zustand 以其簡潔性和靈活性,讓我在開發中感到滿意,在專案需要協助和交接的過程,大多數時候不太需要多餘的解釋,接手的人不論資淺資深都可以快速的上手,使得團隊協作更加順利,隨著開發技術的演進,選擇合適的狀態管理工具變得至關重要,而 Zustand 無疑是當前最佳的選擇之一。


喜歡這篇文章,請幫忙拍拍手喔 🤣