在 React 出現的最初幾年,其實沒有 Redux、沒有 MobX、沒有 Zustand
大家都用 setState()useState(),照樣能寫出功能完整的應用程式。

那麼,問題來了:

有了 useStateuseReducerContext APICustom 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 寫出乾淨架構,
那你已經具備狀態管理的本質功力。

外部套件不是「必須」,而是**「可維護性」與「開發體驗」的升級工具**。