--- tags: [React, State Management, Context, Zustand, Redux, Frontend] create time: 2026-04-29 22:08 --- # 状态管理 ## 概述 当组件树变得复杂,跨层级共享状态、服务器数据同步和状态变更追踪成为挑战。本文档对比 Context API、Zustand 和 Redux Toolkit 三种主流方案,帮助你在不同场景下做出正确选择。 ## 选型决策图 ```mermaid graph TD A["需要全局状态吗?"] -->|"否"| B["useState / useReducer ✅"] A -->|"是"| C["状态类型?"] C --> D["纯 UI 状态
(主题、菜单展开、模态框)"] C --> E["服务器数据 / 异步缓存"] C --> F["应用级业务状态
(用户信息、购物车、权限)"] D --> G["Context API ✅"] E --> H["TanStack Query / SWR ✅"] F --> I["状态规模?"] I --> J["小型 (< 5 store)"] I --> K["中大型"] J --> L["Zustand ✅"] K --> M["Redux Toolkit + RTK Query ✅"] style G fill:#4FC08D,color:#fff style H fill:#F5A87D,color:#000 style L fill:#61DAFB,color:#000 style M fill:#764abc,color:#fff ``` ## Context API —— 轻量传递 ```tsx const AuthContext = createContext<{ user: User | null; login: (u: User) => void }>({ user: null, login: () => {}, }); // Provider function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState(null); const login = (u: User) => setUser(u); return {children}; } // Consumer function Profile() { const { user, login } = useContext(AuthContext); return
Hello, {user?.name}
; } ``` ### Context 的性能局限 | 问题 | 原因 | 解决方案 | |------|------|----------| | value 变化时所有消费组件重渲染 | Context Value 引用每次都是新的 | 拆分多个 Context / 用 reducer 保持 dispatch 引用稳定 | | 不支持 selector | 没有 "只取子字段" 的机制 | 手动封装或使用第三方库 | | SSR hydration mismatch | 客户端与初始值不一致 | 延迟消费或用 useEffect 包裹 | ## Zustand —— 轻量级现代方案 ```ts import { create } from "zustand"; interface StoreState { count: number; users: User[]; increment: () => void; fetchUsers: () => Promise; } const useStore = create((set, get) => ({ count: 0, users: [], increment: () => set(state => ({ count: state.count + 1 })), fetchUsers: async () => { const res = await fetch("/api/users"); const data = await res.json(); set({ users: data }); }, })); // 组件中使用 function Counter() { // ✅ 只订阅 count —— 其他状态变化不会触发此组件重渲染 const count = useStore(s => s.count); const increment = useStore(s => s.increment); return ; } // 批量更新 useStore.setState(({ count }) => ({ count: count + 1, flag: true })); ``` ### Zustand 的优势 | 特性 | Zustand | Context | |------|---------|---------| | Bundle size | ~1KB | 内置 React(~0) | | Selector 支持 | ✅ 精确订阅 | ❌ 全部消费者都重渲染 | | TypeScript 推断 | 完善 | 需手动标注 | | DevTools | 原生支持 | 无 | | 中间件扩展 | persist、immer、devtools | 需额外封装 | ## Redux Toolkit —— 企业级方案 ```ts import { createSlice, configureStore, useDispatch, useSelector } from "@reduxjs/toolkit"; interface CounterSlice { value: number; status: "idle" | "loading" | "succeeded" | "failed"; } const counterSlice = createSlice({ name: "counter", initialState: { value: 0, status: "idle" } as CounterSlice, reducers: { incremented: state => { state.value += 1; }, // ✅ Immer:直接 mutate! fetchedAsync: { pending: state => { state.status = "loading"; }, fulfilled: (state, action) => { state.status = "succeeded"; state.value = action.payload.value; }, rejected: state => { state.status = "failed"; }, }, }, }); export const { incremented, fetchedAsync } = counterSlice.actions; const store = configureStore({ reducer: { counter: counterSlice.reducer }, }); ``` ```tsx function CounterComponent() { const dispatch = useDispatch(); const count = useSelector((s: AppState) => s.counter.value); return ; } ``` ### RTK Query —— 内置数据获取 ```ts import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; const api = createApi({ reducerPath: "api", baseQuery: fetchBaseQuery({ baseUrl: "/api" }), endpoints: build => ({ getUsers: build.query({ query: () => "/users" }), updateUser: build.mutation>({ query: ({ id, ...patch }) => ({ url: `/users/${id}`, method: "PATCH", body: patch }), }), }), }); export const { useGetUsersQuery, useUpdateUserMutation } = api; ``` ## 三框架横向对比 | 维度 | Context API | Zustand | Redux Toolkit | |------|-------------|---------|---------------| | Bundle 大小 | ~0KB(内置) | ~1KB | ~15KB | | 学习曲线 | 低 | 低 | 中 | | Selector | ❌ | ✅ | ✅(useSelector) | | Immutable | ❌ | ❌(但可用 immer middleware) | ✅(Immer 内置) | | 调试工具 | ❌ | ✅ | ✅ Redux DevTools | | 服务端渲染 | ⚠️ 需手动处理 | ✅ | ✅ | | 适用规模 | 小型项目 | 中小/中型 | 大型企业级 | ## 常见反模式 > [!warning] 以下做法应避免 ```tsx // ❌ 把所有东西塞进一个 store const useBigStore = create(() => ({ user: ..., theme: ..., sidebarOpen: ..., notifications: ..., cart: ..., preferences: ..., // 20+ 个状态 })); // ✅ 按功能拆分为多个 store const useUserStore = create(...) const useThemeStore = create(...) // ❌ 在 state 中存储服务器返回的数据却不做缓存 const [data, setData] = useState(fetch(...)) // 页面切换丢失,重复请求 // ✅ 使用 TanStack Query 等专用数据获取库处理缓存和失效 const { data } = useQuery({ queryKey: ["users"], queryFn: fetchUsers }); ``` ## 关联笔记