This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/REACT/3. 生态工具篇/09-状态管理.md
T
2026-04-29 22:19:51 +08:00

6.1 KiB
Raw Blame History

tags, create time
tags create time
React
State Management
Context
Zustand
Redux
Frontend
2026-04-29 22:08

状态管理

概述

当组件树变得复杂,跨层级共享状态、服务器数据同步和状态变更追踪成为挑战。本文档对比 Context API、Zustand 和 Redux Toolkit 三种主流方案,帮助你在不同场景下做出正确选择。

选型决策图

graph TD
    A["需要全局状态吗?"] -->|"否"| B["useState / useReducer ✅"]
    A -->|"是"| C["状态类型?"]
    
    C --> D["纯 UI 状态<br/>(主题、菜单展开、模态框)"]
    C --> E["服务器数据 / 异步缓存"]
    C --> F["应用级业务状态<br/>(用户信息、购物车、权限)"]
    
    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 —— 轻量传递

const AuthContext = createContext<{ user: User | null; login: (u: User) => void }>({
  user: null,
  login: () => {},
});

// Provider
function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  
  const login = (u: User) => setUser(u);
  
  return <AuthContext.Provider value={{ user, login }}>{children}</AuthContext.Provider>;
}

// Consumer
function Profile() {
  const { user, login } = useContext(AuthContext);
  return <div>Hello, {user?.name}</div>;
}

Context 的性能局限

问题 原因 解决方案
value 变化时所有消费组件重渲染 Context Value 引用每次都是新的 拆分多个 Context / 用 reducer 保持 dispatch 引用稳定
不支持 selector 没有 "只取子字段" 的机制 手动封装或使用第三方库
SSR hydration mismatch 客户端与初始值不一致 延迟消费或用 useEffect 包裹

Zustand —— 轻量级现代方案

import { create } from "zustand";

interface StoreState {
  count: number;
  users: User[];
  increment: () => void;
  fetchUsers: () => Promise<void>;
}

const useStore = create<StoreState>((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 <button onClick={increment}>Count: {count}</button>;
}

// 批量更新
useStore.setState(({ count }) => ({ count: count + 1, flag: true }));

Zustand 的优势

特性 Zustand Context
Bundle size ~1KB 内置 React(~0)
Selector 支持 ✅ 精确订阅 ❌ 全部消费者都重渲染
TypeScript 推断 完善 需手动标注
DevTools 原生支持 无
中间件扩展 persist、immer、devtools 需额外封装

Redux Toolkit —— 企业级方案

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 },
});
function CounterComponent() {
  const dispatch = useDispatch<AppDispatch>();
  const count = useSelector((s: AppState) => s.counter.value);
  
  return <button onClick={() => dispatch(incremented())}>{count}</button>;
}

RTK Query —— 内置数据获取

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

const api = createApi({
  reducerPath: "api",
  baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
  endpoints: build => ({
    getUsers: build.query<User[], void>({ query: () => "/users" }),
    updateUser: build.mutation<User, Partial<User>>({
      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] 以下做法应避免

// ❌ 把所有东西塞进一个 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 });

关联笔记