狀態管理的本質問題

React 的所有狀態管理選擇,都是在解決同一個問題:多個元件需要共享和同步同一份資料時,怎麼管理

如果只是單一元件內的資料(例如一個計數器),useState 就夠了,不需要任何狀態管理庫。

問題來了:當元件層級加深、共享狀態的元件越來越多,傳遞 props 的鏈越來越長,這就是「Prop Drilling」問題,也是引入狀態管理的時機。

useState + Props(適合小型應用)

完全依靠 React 內建的 useState 和 props 傳遞。

適合:單頁面應用、只有 2–3 層的元件深度、不需要全域共享的複雜狀態。

不適合:需要在頁面中多個不相關的元件共享同一份狀態(例如:導覽列的登入狀態需要影響頁面深處的按鈕樣式)。

React Context(適合中型應用的少量全域狀態)

React Context 解決 Prop Drilling,讓你跳過中間層直接傳遞資料給深層元件。

適合情境:

主題切換(深色 / 淺色模式):全局只有一個值,不頻繁更新。

語言設定(i18n):靜態的設定值。

登入用戶資訊:設定一次、大量讀取、很少更新。

Context 的效能陷阱:Context 的 value 每次改變,所有消費該 Context 的元件都會重新渲染——即使某個元件只用了 value 中的一個屬性。

所以:不要把高頻率更新的狀態(例如随用戶操作即時變更的 UI 狀態)放進 Context。Context 適合讀多寫少的全域設定。

Zustand(適合大多數中到大型應用)

Zustand 是目前 React 生態系中最受歡迎的輕量狀態管理庫之一,API 極為簡單。

核心特點:

只有你訂閱的那部分狀態改變,你的元件才會重新渲染(細粒度的選擇器,解決 Context 的效能問題)。

不需要 Provider 包裹整個應用,直接在任何元件中 import 使用。

支援中間件(persist: 持久化到 localStorage;devtools: 整合 Redux DevTools)。

建立 store 的方式非常直觀,學習曲線低:使用 create 函式定義 store,在 store 中同時放入狀態(state)和修改函式(actions),元件中用 useStore hook 取得需要的部分。

適合:需要全局狀態但不想要 Redux 複雜度的大多數應用,是目前接案項目的首選。

Redux Toolkit(適合大型企業應用)

Redux(搭配官方的 Redux Toolkit)是最成熟的 React 狀態管理方案,帶來嚴格的架構規範:Action → Reducer → Store 的單向資料流。

適合:大型多人協作的企業應用(嚴格的架構讓多人維護更容易)、需要複雜的時間旅行除錯(Redux DevTools)、已有 Redux 的既有大型代碼庫。

不適合:小到中型應用,Redux Toolkit 的 boilerplate 雖然比舊版 Redux 少很多,但對比 Zustand 仍然更冗長。

Server State 和 Client State 的分離

現代 React 應用通常需要管理兩種截然不同的狀態:

Server State(伺服器狀態):從 API 或資料庫取得的資料,需要處理 loading、error、快取、重新獲取、失效等問題。最佳工具:TanStack Query(React Query)或 SWR。

Client State(客戶端狀態):純 UI 狀態(模態框是否開啟、選中的標籤、過濾設定),只存在客戶端,不需要從服務器獲取。最佳工具:useState、Zustand。

這兩種狀態混在一起用 Redux 管理,是過去很多大型應用架構複雜的根源。把 Server State 交給 TanStack Query 處理,Client State 交給 Zustand,是更現代的架構方式。

決策樹

需要狀態管理嗎?只有單一元件用 → 直接用 useState,不需要解決方案。

跨 2–3 層的少數元件共享,低頻更新 → React Context 足夠。

多個不相關的元件需要共享複雜狀態 → Zustand(首選)。

大型多人協作、或已有 Redux 的既有項目 → Redux Toolkit。

從 API 取得的資料、需要快取和同步 → TanStack Query/SWR(配合上述任何方案)。