在 React 出現的最初幾年,其實沒有 Redux、沒有 MobX、沒有 Zustand。
大家都用 setState() 或 useState(),照樣能寫出功能完整的應用程式。
那麼,問題來了:
有了
useState、useReducer、Context API和Custom Hook,
我們還需要外部狀態管理工具嗎?
這篇,我們不談抽象理論,只講實際開發中痛不痛、為什麼痛、誰幫你止痛。
一、React 自帶的狀態管理,其實已經很強
React 的哲學是 「UI = f(state)」。
意思是:只要你的狀態設計得好,UI 自然會反映出正確的結果。
四大內建利器
| 工具 | 功能核心 | 適合範圍 |
|---|---|---|
useState | 管理單一元件內的狀態 | 局部互動、簡單表單 |
useReducer | 管理複雜邏輯或多步驟狀態 | 中型元件、事件驅動更新 |
Context API | 跨層共享狀態 | 全域設定、登入、主題 |
| Custom Hook | 封裝可重用邏輯 | 模組化業務邏輯(如 useAuth) |
這四者搭配起來,其實能做到:
- 狀態集中管理(透過 Context)
- 關注點分離(透過 hook)
- 重用 callback(封裝邏輯)
- 減少 prop drilling(由 Context 解決)
👉 對於小型或中型專案來說,這些已經足夠了。
二、問題是:隨著專案長大,它開始「痛」
當應用成長、頁面變多、狀態交錯時,幾個痛點會浮現出來。
1️⃣ Context API 的「全域重渲染」
React 的 Context 是廣播式的:
Provider 的值一改變,所有 consumer 都會重渲染。
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Navbar />
<Sidebar />
<Main />
</ThemeContext.Provider>
);
}即使 Sidebar 根本沒用到 theme,也會跟著一起 re-render。
這就是 Context 的天生限制。
外部套件怎麼解?
Zustand、Recoil、Jotai:改用「訂閱模型」(subscription-based)
只讓實際用到狀態的元件更新
👉 結果:效能提升、畫面更新更精準。
2️⃣ Context Provider 地獄
當你有多個不同邏輯的全域狀態時(auth、UI、data),
你可能會寫出這樣的巢狀結構:
<AuthProvider>
<UIProvider>
<DataProvider>
<App />
</DataProvider>
</UIProvider>
</AuthProvider>很快你會發現:
- 測試不方便:要測試
App元件,必須先包裝所有 Provider,測試 setup 變得複雜 - 型別推論困難:當你有多個 Context 時,每個 Context 都需要定義自己的型別。但問題是,當你在元件裡使用
useContext(AuthContext)時,TypeScript 無法自動推論出這個 Context 的型別,你必須手動指定型別,例如useContext(AuthContext) as AuthContextType,這讓程式碼變得冗長且容易出錯 - 維護麻煩:如果把
DataProvider移到AuthProvider外面,整個應用就會因為找不到user資料而壞掉
外部套件怎麼解?
- Zustand:單一 store + 模組化 slice
- Recoil / Jotai:以原子(atom)為單位,自然組合
- Redux Toolkit:集中管理多 reducer,結構清晰
3️⃣ Debug 不可視、狀態變化難追蹤
當你用 hook / context 管理狀態時,
很難知道「誰」改了 state,「什麼時候」改的。
外部套件補上了這塊:
- Redux DevTools → 時間旅行除錯
- Zustand DevTools → 狀態快照回放
- XState → 狀態轉換圖形化
👉 結果:團隊除錯變輕鬆、狀態更可預測。
4️⃣ 異步邏輯的痛苦
原生 useEffect + fetch 對於資料同步、快取、重試、取消等狀況非常脆弱。
外部解法:
- React Query / TanStack Query 專職「伺服器狀態」
- Redux Toolkit Query 整合 API 請求
- Zustand / Jotai 支援 async atom
👉 本地狀態 vs. 伺服器狀態清楚分層。
三、所以外部狀態管理到底在幹嘛?
簡單說:
它們不是「比 React 更聰明」,
而是「幫你避開 React 不擅長的地方」。
幾乎所有外部套件底層仍然使用:
- React Context
useSyncExternalStore(訂閱更新)- Immutable 或 Proxy-based 狀態追蹤
它們幫你:
- 避免不必要的 re-render
- 提供可視化的除錯工具
- 模組化 store 結構
- 提高團隊協作與測試性
四、什麼時候該引入外部狀態管理?
| 專案規模 | 建議方案 | 理由 |
|---|---|---|
| 小型(個人作品) | useState + Context + Hook | 輕量、足夠 |
| 中型(多頁應用) | Zustand / Jotai / Recoil | 結構簡潔、效能好 |
| 大型(多人協作) | Redux Toolkit / XState | 可除錯、可測試 |
| 高互動應用 | XState / Signals | 對狀態轉換控制更細 |
結語:先學會 React 的狀態,再選擇更好的工具
如果你能用 Context + Hook 寫出乾淨架構,
那你已經具備狀態管理的本質功力。
外部套件不是「必須」,而是**「可維護性」與「開發體驗」的升級工具**。