技術 6 分鐘閱讀
React 狀態管理方案比較:該選哪一個?
前言
「React 的狀態管理該用什麼?」這是我最常被問到的問題之一。選擇太多反而讓人困惑,這篇文章幫你整理各個方案的優缺點,讓你做出最適合的選擇。
先搞清楚:你真的需要狀態管理嗎?
在決定用什麼工具之前,先問自己:
- 狀態是否需要跨多個元件共享?
- useState 和 props drilling 是否已經夠用?
- 狀態的複雜度真的需要外部工具嗎?
很多中小型專案其實用 React 內建的功能就夠了。不要為了用工具而用工具。
方案一:Context API + useReducer
適合場景
- 中小型應用
- 不常變動的全域狀態(主題、語言、使用者資訊)
- 不想引入額外套件
實作範例
// contexts/AuthContext.tsx
interface AuthState {
user: User | null;
isLoading: boolean;
}
type AuthAction =
| { type: 'LOGIN'; payload: User }
| { type: 'LOGOUT' }
| { type: 'SET_LOADING'; payload: boolean };
const authReducer = (state: AuthState, action: AuthAction): AuthState => {
switch (action.type) {
case 'LOGIN':
return { ...state, user: action.payload, isLoading: false };
case 'LOGOUT':
return { ...state, user: null, isLoading: false };
case 'SET_LOADING':
return { ...state, isLoading: action.payload };
default:
return state;
}
};
const AuthContext = createContext<{
state: AuthState;
dispatch: React.Dispatch<AuthAction>;
} | null>(null);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [state, dispatch] = useReducer(authReducer, {
user: null,
isLoading: true,
});
return (
<AuthContext.Provider value={{ state, dispatch }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) throw new Error('useAuth must be used within AuthProvider');
return context;
}
缺點
Context 的值一變動,所有消費者都會重新渲染,即使它只用了其中一部分的狀態。對於頻繁變動的狀態(如表單輸入),效能可能會有問題。
方案二:Zustand
適合場景
- 中大型應用
- 需要高效能的狀態更新
- 喜歡簡潔的 API
為什麼我推薦 Zustand
Zustand 是我目前最推薦的狀態管理方案,原因如下:
- API 極簡:學習成本低
- 效能好:自動只重新渲染用到的元件
- 不需要 Provider:減少元件巢狀
- TypeScript 支援好:型別推導完整
實作範例
import { create } from 'zustand';
interface CartStore {
items: CartItem[];
addItem: (product: Product) => void;
removeItem: (productId: number) => void;
updateQuantity: (productId: number, quantity: number) => void;
clearCart: () => void;
totalPrice: () => number;
}
const useCartStore = create<CartStore>((set, get) => ({
items: [],
addItem: (product) =>
set((state) => {
const existing = state.items.find((i) => i.product.id === product.id);
if (existing) {
return {
items: state.items.map((i) =>
i.product.id === product.id
? { ...i, quantity: i.quantity + 1 }
: i
),
};
}
return { items: [...state.items, { product, quantity: 1 }] };
}),
removeItem: (productId) =>
set((state) => ({
items: state.items.filter((i) => i.product.id !== productId),
})),
updateQuantity: (productId, quantity) =>
set((state) => ({
items: state.items.map((i) =>
i.product.id === productId ? { ...i, quantity } : i
),
})),
clearCart: () => set({ items: [] }),
totalPrice: () =>
get().items.reduce((sum, i) => sum + i.product.price * i.quantity, 0),
}));
在元件中使用:
function CartButton() {
// 只訂閱 items.length,不會因為其他狀態變更而重新渲染
const itemCount = useCartStore((state) => state.items.length);
return <button>購物車 ({itemCount})</button>;
}
function CartTotal() {
const totalPrice = useCartStore((state) => state.totalPrice());
return <p>總計:NT$ {totalPrice.toLocaleString()}</p>;
}
方案三:Redux Toolkit
適合場景
- 大型企業應用
- 需要嚴格的狀態管理規範
- 團隊已經熟悉 Redux 生態系
為什麼不再首推 Redux
Redux Toolkit 已經大幅簡化了 Redux 的使用方式,但對於大多數接案專案來說,它仍然太重了:
- 需要理解 slice、action、reducer 的概念
- 設定檔案比較多
- 學習曲線比 Zustand 陡
如果不是大型專案或團隊沒有 Redux 經驗,我通常不會選擇它。
方案四:TanStack Query(React Query)
適合場景
- 需要管理伺服器端狀態
- 需要快取、自動重新取得、樂觀更新
要注意
TanStack Query 是管理伺服器端狀態的工具,不是一般的狀態管理工具。它處理的是 API 資料的快取和同步,跟 Zustand 或 Redux 是不同層級的東西。
實務上我常常 Zustand + TanStack Query 搭配使用:
- Zustand:管理 UI 狀態(Modal 開關、篩選條件等)
- TanStack Query:管理 API 資料(商品列表、使用者資料等)
// 用 TanStack Query 管理 API 資料
const { data: products, isLoading } = useQuery({
queryKey: ['products', filters],
queryFn: () => api.getProducts(filters),
});
// 用 Zustand 管理 UI 狀態
const filters = useFilterStore((state) => state.filters);
const setFilter = useFilterStore((state) => state.setFilter);
方案比較表
| 特性 | Context | Zustand | Redux Toolkit | TanStack Query |
|---|---|---|---|---|
| 學習成本 | 低 | 低 | 中 | 中 |
| 效能 | 普通 | 好 | 好 | 好 |
| 套件大小 | 0 | 1KB | 11KB | 13KB |
| DevTools | 無 | 有 | 有 | 有 |
| 適合規模 | 小 | 中大 | 大 | 所有 |
我的建議
- 小型專案:Context API 就夠了
- 中型接案專案:Zustand + TanStack Query
- 大型企業專案:Redux Toolkit + TanStack Query
- 不確定的話:先用 Zustand,它的遷移成本最低
結語
狀態管理沒有銀彈,關鍵是選擇適合專案規模和團隊經驗的方案。我個人在接案中最常用的組合是 Zustand + TanStack Query,簡單好用又夠彈性。希望這篇比較能幫你做出更好的選擇。
#React #狀態管理 #Zustand #Redux