商店現已上線

我們打造用於網站、AI 智慧體和 LLM 訓練的 Workstation AI 機器——線上配置併購買。

獲取同行價格
AI 產品
AI 實驗室OpenAI 智慧體Claude 代理Grok BotWorkstation AI 原生 CRM營銷所有產品
AI 解決方案
應用型 AIAI 智慧體生成式 AIAI 自動化AI 技能提升中小企業 AI 套餐企業 AI 實驗室AI 工作站NVIDIA 硬體與軟體NVIDIA 加速計算私有 AIGPU 私有云GPU 叢集邊緣 AI行業 AI
AI 服務
平臺現代化數字工程資料基礎與 AI自主運營AI 諮詢DevOps 自動化網路安全軟體開發智慧體構建MLOps 搭建GPU 租賃GPU 私有云
關於我們
公司我們的故事合作伙伴客戶案例
AI 資料中心
AI 資料中心GPU 私有云GPU 叢集GPU 租賃
AI 洞察Jev 系列基準測試實驗室模型選擇
技術說明
文件
Linux 作業系統UbuntuRed Hat Enterprise LinuxSUSE LinuxAlpine LinuxDebian、Rocky 與 AlmaLinux
AI 雲平臺AWS 與 Amazon BedrockGoogle Cloud 與 Vertex AIMicrosoft Azure 與 AI FoundryGPU 雲與算力市場
私有 AI 資料中心面向律師事務所面向會計師事務所本地部署平臺全面安全的雲
工具Ring PromoterJobshout
新聞
部落格
聯絡我們登入

面向現代企業的 AI 工作站、AI 多智慧體軟體、GPU 基礎設施和智慧代理解決方案。

聯絡我們

AI 解決方案

應用型 AIAI 智慧體生成式 AIAI 自動化AI 技能提升中小企業 AI 套餐企業 AI 實驗室行業 AI

產品

所有產品Workstation AI native CRM 與 ERP營銷OpenAI 智慧體Ring PromoterJobshout

公司

關於我們公司AI 資料中心為什麼選擇Workstation合作伙伴客戶案例定價聯絡TenthMatrix

資源

AI 洞察技術說明文件Linux 作業系統AI 雲平臺私有 AI 資料中心部落格搜尋網站地圖
英國總部
77-79 Marlowes, Hemel Hempstead HP1 1LF路線指引 — 從 M25 外環倫敦 20 號出口駛出公司編號: 11641870週一至週五:上午 9:00 - 下午 6:00 GMT
+44 7515 356 146
比利時辦公室
Workstation SRL, Rue Vanderkindere 34, 1180 Uccle, BrusselsBE 0751.518.683週一至週五:上午 9:00 - 下午 6:00 CET
+32 492 45 67 46
印度辦公室
#159 Sector 9, Pocket 1, DDA Flats, 110077 Dwarka, New Delhi
+91 98881 98841
台灣辦公室
3F, No. 2-4, Aisi Rd., Ren'ai Dist., Keelung City 200, Taiwan基隆市仁愛區愛四路2之4號3樓週一至週五:上午 9:00 - 下午 6:00 臺灣時間(GMT+8)
+886 926 892 701+32 492 45 67 46

© 2026 Workstation AI。保留所有權利。

隱私資料刪除Cookie服務條款網站地圖

NVIDIA、NVIDIA 標誌、RTX、RTX PRO、Blackwell、CUDA、NVLink、MGX、Omniverse、Spectrum-X、Quantum、BlueField、DGX 與 HGX 為 NVIDIA Corporation 在美國及其他國家的商標及/或註冊商標。AMD、AMD Arrow 標誌、EPYC、Instinct、Ryzen 與 Threadripper 為 Advanced Micro Devices, Inc. 的商標。Dell 與 PowerEdge 為 Dell Inc. 或其子公司的商標。Intel 與 Xeon 為 Intel Corporation 或其子公司的商標。AMAX、AceleMax 與 LiquidMax 為 AMAX Engineering Corporation 的商標。所有其他產品名稱、標誌與品牌均為其各自所有者之財產。我們使用它們僅為識別我們所描述、供應或支援的產品;使用這些名稱並不表示其所有者為我們背書。

Loading blog...

首頁 / 部落格
WebFrontendReact

React 中的高階狀態管理:為工作選擇正確的工具

Redux Toolkit、Zustand、Jotai、TanStack Query 和 React Context 的實用指南 — 包含真實的架構模式和效能技術

Balinder Walia2025年5月27日6 min read
React state management

狀態管理是每個重要 React 應用程式的核心。如果做得對,你的程式碼庫就能優雅地擴充套件;如果出錯,你最終會陷入道具鑽探的噩夢、陳舊的 UI 和元件重新渲染而被遺忘。好訊息是 React 生態系統已經顯著成熟。您現在擁有豐富的專用工具選單 - Redux Toolkit、Zustand、Jotai、TanStack Query 和內建 Context API - 每個工具都針對特定類別的問題進行了最佳化。

本文深入探討了每個解決方案,透過具體示例解釋了權衡,併為您提供了一個可以立即應用於您自己的專案的決策框架。

瞭解不同型別的狀態

React 狀態管理架構元件樹應用程式頁AB頁比較比較比較比較useState 住在這裡每個元件本地useState / useReducer範圍:單一元件下拉選單、輸入、表單欄位、切換React 背景範圍:子樹/應用程式範圍(低頻)主題、區域設定、身份驗證使用者、功能標誌Redux 工具包範圍:使用 DevTools 的全球商店複雜狀態、中介軟體、時間旅行祖斯坦輕量級全球商店佐泰原子態細粒度TanStack 查詢 — 伺服器狀態抓取、快取、同步、後臺重新整理、樂觀更新API 響應、分頁列表、實時資料本地國家範圍全球使用狀態背景祖斯坦佐泰終極版

在使用任何庫之前,有必要準確瞭解您實際管理的狀態型別。合併不同的狀態類別是大多數過度設計的 React 應用程式的根本原因。

  • 本地使用者介面狀態 — 下拉選單是否開啟、哪個選項卡處於活動狀態、受控輸入的當前值。該狀態由單個元件或小子樹擁有。
  • 共享客戶端狀態 — 多個可能遙遠的元件需要讀取或寫入的資料:當前經過身份驗證的使用者、主題首選項、購物車。
  • 伺服器狀態 — 源自伺服器的資料本質上是非同步的,需要快取失效、後臺重新獲取和載入/錯誤生命週期管理。
  • URL狀態 — 過濾器、分頁和搜尋詞應在頁面重新整理後繼續存在並可透過連結共享。
  • 表單狀態 — 受控輸入、驗證錯誤和提交狀態,通常最好由專用庫(例如 React Hook Form)處理。

您能做的最有影響力的一件事就是抵制將每種狀態類別倒入一家全球商店的衝動。每個類別都有不同的一致性要求、不同的生命週期和不同的更新頻率。

React 上下文:內建選項

Context API 隨 React 一起提供,不需要額外的依賴項。它適用於不經常更改且被許多元件消耗的狀態——典型的例子是主題、區域設定和經過身份驗證的使用者物件。

// AuthContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';

interface AuthState {
  user: User | null;
  login: (credentials: Credentials) => Promise<void>;
  logout: () => void;
}

const AuthContext = createContext<AuthState | null>(null);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);

  const login = async (credentials: Credentials) => {
    const user = await authService.login(credentials);
    setUser(user);
  };

  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('useAuth must be used inside AuthProvider');
  return ctx;
}

上下文效能陷阱

上下文有一個眾所周知的效能特徵,這讓許多開發人員感到困惑:每當上下文值引用發生變化時,每個消費者都會重新渲染。如果您在單個上下文中儲存大型且頻繁變化的物件,則會在整個樹上觸發不必要的重新渲染。

緩解策略是:

  • 按更新頻率分割上下文。將使用者物件保留在一個上下文中,將 UI 首選項保留在另一上下文中。
  • 記住值物件 useMemo 因此,只有當資料實際發生變化時,引用才會發生變化。
  • 使用 React.memo 當消費者元件關心的道具沒有改變時,他們會跳過重新渲染。

誠實的結論是,Context 對於低頻全域性狀態非常出色,但它不是通用的狀態管理解決方案。一旦您發現自己正在編寫複雜的記憶來解決 Context 的重新渲染行為,那麼就該使用專用的庫了。

Redux 工具包:成熟的企業選擇

Redux 工具包 — 單向資料流動作創造者派遣中介軟體thunk / saga / 記錄器減速機商店單一事實來源選擇器元件UI 重新渲染排程(動作)經過過程動作返回新狀態匯出資料更新檢視使用者互動Redux 開發工具時間旅行除錯和狀態檢查

Redux 因樣板檔案而聞名——這是它在 Toolkit 時代之前贏得的聲譽。 Redux Toolkit (RTK) 消除了儀式,同時保留了 Redux 的強大之處:具有嚴格單向資料流的單一、可預測、可檢查的狀態樹。

RTK船舶 createSlice,它從單個定義生成動作建立者和減速器,並在底層使用 Immer,這樣你就可以編寫看起來命令式但實際上是不可變應用的突變。

// features/cart/cartSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface CartItem {
  id: string;
  name: string;
  quantity: number;
  price: number;
}

interface CartState {
  items: CartItem[];
  coupon: string | null;
}

const initialState: CartState = { items: [], coupon: null };

export const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {
    addItem(state, action: PayloadAction<CartItem>) {
      const existing = state.items.find(i => i.id === action.payload.id);
      if (existing) {
        existing.quantity += action.payload.quantity;
      } else {
        state.items.push(action.payload);
      }
    },
    removeItem(state, action: PayloadAction<string>) {
      state.items = state.items.filter(i => i.id !== action.payload);
    },
    applyCoupon(state, action: PayloadAction<string>) {
      state.coupon = action.payload;
    },
  },
});

export const { addItem, removeItem, applyCoupon } = cartSlice.actions;
export default cartSlice.reducer;

RTK 查詢:Redux 內的伺服器狀態

RTK 附帶了一個名為 RTK Query 的配套產品,它直接在 Redux 儲存中處理伺服器狀態。它從端點定義自動生成 React 掛鉤並管理快取、失效和樂觀更新。

// services/productsApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const productsApi = createApi({
  reducerPath: 'productsApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  tagTypes: ['Product'],
  endpoints: (builder) => ({
    getProducts: builder.query<Product[], void>({
      query: () => '/products',
      providesTags: ['Product'],
    }),
    updateProduct: builder.mutation<Product, Partial<Product>>({
      query: ({ id, ...patch }) => ({
        url: `/products/${id}`,
        method: 'PATCH',
        body: patch,
      }),
      invalidatesTags: ['Product'],
    }),
  }),
});

export const { useGetProductsQuery, useUpdateProductMutation } = productsApi;

當您需要透過 Redux DevTools 進行時間旅行除錯時,當多個團隊需要透過強制約定為共享狀態模型做出貢獻時,或者當您擁有受益於中介軟體(例如)的複雜跨切片業務邏輯時,Redux Toolkit 是正確的選擇 redux-saga 或 redux-observable.

Zustand:無需儀式的輕量級全域性狀態

Zustand採取了一種完全不同的方法。沒有提供者,沒有減速器,也沒有操作型別。您將儲存定義為具有狀態和方法的純 JavaScript 物件,然後使用單個鉤子使用它。整個 API 適合在一個螢幕上。

// stores/useCartStore.ts
import { create } from 'zustand';
import { persist, devtools } from 'zustand/middleware';

interface CartStore {
  items: CartItem[];
  addItem: (item: CartItem) => void;
  removeItem: (id: string) => void;
  totalPrice: () => number;
  clear: () => void;
}

export const useCartStore = create<CartStore>()(
  devtools(
    persist(
      (set, get) => ({
        items: [],

        addItem: (item) =>
          set((state) => {
            const existing = state.items.find((i) => i.id === item.id);
            if (existing) {
              return {
                items: state.items.map((i) =>
                  i.id === item.id
                    ? { ...i, quantity: i.quantity + item.quantity }
                    : i
                ),
              };
            }
            return { items: [...state.items, item] };
          }),

        removeItem: (id) =>
          set((state) => ({ items: state.items.filter((i) => i.id !== id) })),

        totalPrice: () =>
          get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),

        clear: () => set({ items: [] }),
      }),
      { name: 'cart-storage' }
    )
  )
);

精細訂閱和效能

Zustand透過基於選擇器的訂閱解決了Context的重新渲染問題。僅當元件選擇的狀態切片發生更改時,元件才會重新渲染。

// Only re-renders when items.length changes, not on price changes
const itemCount = useCartStore((state) => state.items.length);

// Only re-renders when the total changes
const total = useCartStore((state) => state.totalPrice());

Zustand 的中介軟體生態系統涵蓋永續性 localStorage、DevTools 整合、Immer 式突變和 URL 同步。對於想要 Redux 級別的功能而無需 Redux 級別的設定成本的團隊來說,它是首選。

Jotai:受反衝啟發的原子態

Jotai 將狀態模型描述為一張原子圖——可以相互派生的微小的、可組合的狀態單元。該模型對於動態狀態特別強大:想象一個編輯器節點列表,其中每個節點都有自己獨立的選擇/焦點狀態,或者一個複雜的形式,其中欄位可見性取決於其他欄位值。

// atoms/filterAtoms.ts
import { atom, selector } from 'jotai';

export const searchTermAtom = atom('');
export const categoryAtom = atom<string | null>(null);
export const productsAtom = atom<Product[]>([]);

// Derived atom — recomputes only when its dependencies change
export const filteredProductsAtom = atom((get) => {
  const products = get(productsAtom);
  const term = get(searchTermAtom).toLowerCase();
  const category = get(categoryAtom);

  return products.filter((p) => {
    const matchesTerm = p.name.toLowerCase().includes(term);
    const matchesCategory = category === null || p.category === category;
    return matchesTerm && matchesCategory;
  });
});
// ProductList.tsx
import { useAtom, useAtomValue } from 'jotai';

function ProductList() {
  const [searchTerm, setSearchTerm] = useAtom(searchTermAtom);
  const filtered = useAtomValue(filteredProductsAtom);

  return (
    <>
      <input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
      {filtered.map((p) => <ProductCard key={p.id} product={p} />)}
    </>
  );
}

Jotai 的細粒度反應性模型意味著當搜尋詞發生變化時,只有真正訂閱的元件 filteredProductsAtom 重新渲染。僅訂閱的元件 categoryAtom 未受影響。這使得 Jotai 非常適合具有密集、相互依賴的狀態圖的應用程式。

TanStack 查詢:伺服器狀態的正確歸宿

TanStack Query(以前稱為 React Query)不是一個通用的狀態管理庫 - 它是一個伺服器狀態庫,並且它比生態系統中的其他任何庫都更好地處理該責任。在不使用 TanStack 查詢的情況下在 React 中獲取、快取、同步和更新遠端資料通常意味著在本地狀態或全域性儲存中手動管理載入標誌、錯誤物件和快取失效。 TanStack Query 可自動執行所有這些操作。

// hooks/useProducts.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

export function useProducts(filters: ProductFilters) {
  return useQuery({
    queryKey: ['products', filters],
    queryFn: () => api.getProducts(filters),
    staleTime: 5 * 60 * 1000,   // treat data as fresh for 5 minutes
    gcTime: 10 * 60 * 1000,     // keep unused data in cache for 10 minutes
  });
}

export function useUpdateProduct() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (update: ProductUpdate) => api.updateProduct(update),

    // Optimistic update
    onMutate: async (update) => {
      await queryClient.cancelQueries({ queryKey: ['products'] });
      const previous = queryClient.getQueryData(['products']);
      queryClient.setQueryData(['products'], (old: Product[]) =>
        old.map((p) => (p.id === update.id ? { ...p, ...update } : p))
      );
      return { previous };
    },

    onError: (_err, _update, context) => {
      queryClient.setQueryData(['products'], context?.previous);
    },

    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });
}

查詢鍵陣列是 TanStack Query 的快取原語。共享相同金鑰的任何查詢共享相同的快取條目。當您使某個金鑰無效時,訂閱該金鑰的每個元件都會在後臺自動重新獲取,並在新資料到達時進行更新。

Next.js 中的預取和水化

在 Next.js 應用程式中,您可以在伺服器上預取查詢並將其脫水到 HTML 有效負載中,從而完全消除初始頁面渲染的載入旋轉器。

// app/products/page.tsx (Next.js App Router)
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';

export default async function ProductsPage() {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({
    queryKey: ['products', {}],
    queryFn: () => api.getProducts({}),
  });

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <ProductList />
    </HydrationBoundary>
  );
}

組合庫:實用的架構

真正的應用程式幾乎總是受益於組合多個工具,每個工具負責自己的狀態類別。結構良好的中型到大型 React 應用程式可能如下所示:

  • TanStack查詢 擁有所有伺服器狀態。它獲取、快取和同步遠端資料。沒有其他東西會影響 API 響應。
  • 祖斯坦 擁有全域性客戶端狀態——購物車、經過身份驗證的使用者會話、UI 首選項以及任何對於 Context 而言過於複雜的跨功能協調。
  • React 背景 擁有低流失配置——當前主題、區域設定、功能標誌。
  • useState / useReducer 自己的本地元件狀態 - 模式開啟/關閉、表單欄位值(或用於複雜表單的 React Hook Form)、選項卡選擇。
  • URL 搜尋引數 自己的導航狀態 - 活動過濾器、排序順序、當前頁碼。

這種關注點的分離不僅僅是美觀的。這意味著您的伺服器狀態快取永遠不會被 UI 狀態汙染,您的全域性客戶端儲存永遠不會因 TanStack 查詢會更有效地快取的響應資料而膨脹,並且您的 Context 永遠不會觸發應用程式範圍的重新渲染,因為有人在其中儲存了快速變化的值。

值得了解的效能模式

具有重新選擇功能的記憶選擇器

從 Redux 儲存中派生計算值時,使用 Reselect 以避免在每次渲染時重新計算。 RTK 再匯出 createSelector 從重新選擇。

import { createSelector } from '@reduxjs/toolkit';

const selectItems = (state: RootState) => state.cart.items;

export const selectCartSummary = createSelector(selectItems, (items) => ({
  count: items.reduce((n, i) => n + i.quantity, 0),
  total: items.reduce((sum, i) => sum + i.price * i.quantity, 0),
}));

使用 useTransition 分割渲染

React 18 顆 useTransition 允許您將狀態更新標記為非緊急,以便瀏覽器可以中斷它以處理更高優先順序的工作(例如使用者輸入)。當狀態更改觸發昂貴的重新渲染時,這特別有用。

const [isPending, startTransition] = useTransition();

const handleFilterChange = (value: string) => {
  startTransition(() => {
    setFilterValue(value); // expensive downstream re-render is deferrable
  });
};

避免物件身份問題

幻像重新渲染的一個常見來源是在渲染內建立新的物件或陣列文字。 useMemo 和 useCallback 是你的工具,但只有當你有效能問題的證據時才應用它們——過早的記憶會增加認知開銷,但不會保證好處。

決策框架

為新功能選擇狀態管理方法時,請使用以下決策樹:

  1. 狀態是純粹本地於一個元件還是一棵小子樹?使用 useState 或 useReducer.
  2. 狀態是否表示從伺服器獲取的資料?使用 TanStack查詢.
  3. 樹上的許多元件都需要全域性客戶端狀態嗎?伸手去拿 祖斯坦 除非您已經在 Redux 程式碼庫中。
  4. 狀態是否很少變化並服務於配置目的(主題、區域設定)?使用 React 背景.
  5. 國家是否與許多派生價值觀高度關聯?考慮 佐泰.
  6. 您是否需要時間旅行除錯、嚴格的架構約定或複雜的非同步傳奇?使用 Redux 工具包.

要避免的常見反模式

  • 在 Redux 或 Zustand 中儲存伺服器響應。 讓 TanStack Query 擁有該資料。在全域性儲存中複製它會產生同步錯誤。
  • 將所有東西集中在一個大型商店中。 屬於同一切片的不相關狀態會強制不必要的耦合並使測試變得更加困難。
  • 使用 Context 進行高頻更新。 每個上下文值的更改都會重新呈現所有消費者。對於每次擊鍵或動畫幀都會發生變化的值,Context 是錯誤的工具。
  • 派生元件內部的狀態。 取決於狀態的計算值應該是記憶選擇器或派生原子,而不是在每個渲染上執行的內聯計算。
  • 忽略載入和錯誤狀態。 伺服器狀態本質上是非同步的。 TanStack 查詢表面 isLoading, isError, 和 error 對於每個查詢;使用它們。

結論

React 中的狀態管理環境從未如此健康。您不再需要在沉重的框架和從頭開始構建一切之間進行選擇。 Redux Toolkit 提供企業級可預測性,無需其前身的樣板。 Zustand 只需一小部分設定即可實現相同的功能。 Jotai 解決了細粒度、動態狀態圖中的難題。 TanStack Query 已經明確解決了伺服器狀態問題 — 如果您仍在管理 API 響應 useEffect 和 useState,你有責任移民。

最重要的洞察是分類的:首先確定您正在處理的狀態型別,然後選擇針對該類別最佳化的工具。抵制將所有內容都集中到一個系統中的本能。分層方法——用於伺服器狀態的 TanStack 查詢、用於全域性客戶端狀態的 Zustand、用於配置的上下文以及用於其他所有內容的本地狀態——生成的程式碼庫更容易推理、更容易測試,並且隨著團隊和需求的增長而更易於維護。