Zustand介紹
Zustand 是一個輕量級的狀態管理庫,專為 React 應用程式設計。它提供了簡單且直觀的 API,讓前端工程師可以輕鬆管理應用程式的全局狀態。與其他狀態管理庫(如 Redux)相比,Zustand 的特點是:
-
簡單易用:不需要繁瑣的配置或樣板程式碼,API 直觀且易於上手。
-
靈活:支援任何類型的狀態管理,無論是簡單的數值、物件還是非同步操作。
-
高效:僅重新渲染使用到特定狀態的元件,減少不必要的渲染。
-
無需 Provider:不像 Redux 或 React Context,Zustand 不需要將應用程式包裝在 Provider 元件中。
-
支援非同步操作:可以輕鬆處理 Promise 或 async/await。
-
輕量:程式碼庫小,安裝後對專案的影響極小。
Zustand 的核心概念是使用 create 函數創建一個 store,這個 store 包含狀態和更新狀態的函數。開發者可以透過 React Hook 的方式使用這些狀態,並在任何元件中存取或更新它們。
以下將詳細說明你提供的兩個範例,並以繁體中文、台灣用語和 JavaScript 程式碼完整呈現,確保步驟詳細且易於跟隨。
範例 1:主題切換 Store (useThemeStore)
import { create } from "zustand";
const useThemeStore = create((set) => ({
currentTheme: "sunset",
toggleTheme: () =>
set((state) => ({
currentTheme: state.currentTheme === "sunset" ? "winter" : "sunset",
})),
}));
export default useThemeStore;
說明
這個範例展示了一個簡單的主題切換 store,用於管理應用程式的主題(例如 "sunset" 或 "winter")。以下是程式碼的詳細解析和使用方式:
-
創建 Store:
-
使用 create 函數創建一個 Zustand store,傳入一個回呼函數,該函數接收 set 參數。
-
set 是一個用來更新狀態的函數,類似於 React 的 setState,但更靈活。
-
Store 回傳一個物件,包含狀態 (currentTheme) 和更新狀態的函數 (toggleTheme)。
-
-
狀態和函數:
-
currentTheme:初始值為 "sunset",表示當前主題。
-
toggleTheme:一個函數,用於在 "sunset" 和 "winter" 之間切換主題。
-
使用 set 函數更新 currentTheme,根據當前狀態 (state.currentTheme) 決定新值。
-
set 接受一個函數,該函數接收當前狀態 (state),並回傳要更新的狀態物件。
-
-
-
使用方式:
-
在 React 元件中,可以直接匯入並使用 useThemeStore 來存取狀態或呼叫函數。
-
範例使用程式碼如下:
import React from 'react';
import useThemeStore from './useThemeStore';
function ThemeToggle() {
// 從 store 中解構出 currentTheme 和 toggleTheme
const { currentTheme, toggleTheme } = useThemeStore();
return (
<div>
<p>當前主題:{currentTheme}</p>
<button onClick={toggleTheme}>切換主題</button>
</div>
);
}
export default ThemeToggle;
-
-
步驟解析:
-
匯入 store:在元件中匯入 useThemeStore。
-
使用 Hook:透過 useThemeStore() 取得 currentTheme 和 toggleTheme。
-
渲染狀態:在 JSX 中顯示 currentTheme。
-
觸發更新:點擊按鈕時呼叫 toggleTheme,這會觸發 set 更新狀態,進而重新渲染元件。
-
Zustand 的高效性:只有使用 currentTheme 的元件會重新渲染,無關的元件不會受到影響。
-
-
優點:
-
程式碼簡潔,無需額外的樣板程式碼。
-
狀態邏輯集中在 store 中,易於維護。
-
支援簡單的狀態切換邏輯,適合管理主題、模式等簡單狀態。
-
範例 2:認證管理 Store (useAuthStore)
import { create } from "zustand";
import { openDB } from "idb";
const DB_NAME = "auth-db";
const STORE_NAME = "auth";
const USER_KEY = "user";
// 取得 user
async function getUserFromIDB() {
const db = await openDB(DB_NAME, 1, {
upgrade(db) {
db.createObjectStore(STORE_NAME);
},
});
return await db.get(STORE_NAME, USER_KEY);
}
// 設定 user
async function setUserToIDB(user) {
const db = await openDB(DB_NAME, 1);
await db.put(STORE_NAME, user, USER_KEY);
}
// 移除 user
async function removeUserFromIDB() {
const db = await openDB(DB_NAME, 1);
await db.delete(STORE_NAME, USER_KEY);
}
const useAuthStore = create((set) => ({
isLoggedIn: false,
user: null,
isLoading: true, // 新增
init: async () => {
set({ isLoading: true });
const user = await getUserFromIDB();
set({ isLoggedIn: !!user, user: user || null, isLoading: false });
},
login: async (user) => {
await setUserToIDB(user);
set({ isLoggedIn: true, user });
},
logout: async () => {
await removeUserFromIDB();
set({ isLoggedIn: false, user: null });
},
}));
export default useAuthStore;
說明
這個範例展示了一個更複雜的認證管理 store,結合了 IndexedDB(透過 idb 庫)來持久化儲存使用者資料。以下是詳細解析和使用方式:
-
IndexedDB 輔助函數:
-
getUserFromIDB:從 IndexedDB 中取得使用者資料。
-
使用 openDB 開啟名為 "auth-db" 的資料庫,版本為 1。
-
如果資料庫需要升級(upgrade),則創建一個名為 "auth" 的物件儲存區。
-
使用 db.get 從 "auth" 儲存區中取得鍵為 "user" 的資料。
-
-
setUserToIDB:將使用者資料儲存到 IndexedDB。
- 開啟資料庫並使用 db.put 將 user 物件儲存到鍵 "user"。
-
removeUserFromIDB:從 IndexedDB 中移除使用者資料。
- 開啟資料庫並使用 db.delete 刪除鍵 "user" 的資料。
-
-
創建 Store:
-
使用 create 創建一個名為 useAuthStore 的 store。
-
Store 包含以下狀態和函數:
-
isLoggedIn:布林值,表示使用者是否已登入,初始為 false。
-
user:儲存使用者資料,初始為 null。
-
isLoading:布林值,表示是否正在載入資料,初始為 true。
-
init:非同步函數,用於初始化 store,從 IndexedDB 載入使用者資料。
-
login:非同步函數,處理登入邏輯,將使用者資料儲存到 IndexedDB 並更新狀態。
-
logout:非同步函數,處理登出邏輯,從 IndexedDB 移除資料並重置狀態。
-
-
-
狀態初始化:
-
init 函數在應用程式啟動時呼叫,用於從 IndexedDB 載入使用者資料。
-
首先設置 isLoading: true,表示正在載入。
-
從 IndexedDB 取得使用者資料,根據是否取得資料設置 isLoggedIn 和 user。
-
最後設置 isLoading: false,表示載入完成。
-
-
使用方式:
-
在 React 元件中,可以使用 useAuthStore 來存取認證狀態並執行登入/登出操作。
-
範例使用程式碼如下:
import React, { useEffect } from 'react';
import useAuthStore from './useAuthStore';
function AuthComponent() {
// 從 store 中解構出狀態和函數
const { isLoggedIn, user, isLoading, init, login, logout } = useAuthStore();
// 在元件掛載時初始化 store
//透過 useEffect,我們確保 init 函數在元件掛載時(即元件首次出現在畫面上時)自動執行一次,而不是在每次渲染時都執行
useEffect(() => {
init();
}, [init]);
// 模擬登入
const handleLogin = () => {
const user = { id: 1, name: '使用者名稱' };
login(user);
};
// 模擬登出
const handleLogout = () => {
logout();
};
if (isLoading) {
return <div>載入中...</div>;
}
return (
<div>
{isLoggedIn ? (
<div>
<p>歡迎,{user?.name}</p>
<button onClick={handleLogout}>登出</button>
</div>
) : (
<div>
<p>尚未登入</p>
<button onClick={handleLogin}>登入</button>
</div>
)}
</div>
);
}
export default AuthComponent;
-
-
步驟解析:
-
初始化:在 useEffect 中呼叫 init 函數,確保元件掛載時從 IndexedDB 載入資料。
-
顯示載入狀態:當 isLoading 為 true 時,顯示「載入中」。
-
根據登入狀態渲染:根據 isLoggedIn 決定顯示登入或登出介面。
-
執行登入/登出:點擊按鈕觸發 login 或 logout,這些函數會更新 IndexedDB 和 store 狀態,進而觸發元件重新渲染。
-
持久化儲存:使用 IndexedDB 確保使用者資料在頁面重新整理後依然保留。
-
-
優點:
-
結合 IndexedDB 實現持久化狀態管理,適合需要儲存使用者資料的場景。
-
非同步操作(如資料庫存取)與 Zustand 無縫整合。
-
狀態邏輯清晰,易於維護和擴展。
-
isLoading 狀態方便處理非同步操作的 UI 顯示。
-
總結
-
useThemeStore:一個簡單的主題切換 store,展示 Zustand 的基本用法,適合管理簡單的狀態切換。
-
useAuthStore:一個複雜的認證管理 store,結合 IndexedDB 實現持久化儲存,展示 Zustand 處理非同步操作的能力。
-
Zustand 的優勢:簡單、靈活、高效,無需繁瑣配置,適合前端工程師快速構建狀態管理邏輯。
-
操作步驟:
-
安裝 Zustand:npm install zustand。
-
創建 store 並定義狀態和函數。
-
在元件中匯入並使用 store,透過 Hook 存取狀態或呼叫函數。
-
根據需求結合非同步操作(如 IndexedDB)或簡單的狀態更新。
-