正在加载,请稍候…

2026年React状态管理:Context vs Zustand vs Redux Toolkit

比较React状态管理方案:React Context、Zustand和Redux Toolkit。了解何时使用每种方案、性能考量以及实际场景的代码示例。

2026年React状态管理:Context vs Zustand vs Redux Toolkit

2026年React状态管理:Context vs Zustand vs Redux Toolkit

“我应该用Context、Zustand还是Redux?”这是React开发者最常问的问题之一。到了2026年,生态系统已经成熟,答案也更加清晰。以下是一个全面的比较。

状态管理格局

方案 包体积 复杂度 开发者工具 最佳适用场景
React Context 0KB(内置) 基础 主题、认证、本地化
Zustand 1.1KB Chrome扩展 大多数应用
Redux Toolkit ~11KB 中等 优秀 大型应用、复杂流程
Jotai 3.1KB 原子状态
TanStack Query 13KB 中等 优秀 服务端状态

2026年React状态管理:Context vs Zustand vs Redux Toolkit 插图

React Context — 何时使用

Context是内置的,非常适合不频繁变化的全局数据

// ✅ Context的良好使用:主题
const ThemeContext = createContext<'light' | 'dark'>('light');

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// ✅ Context的良好使用:当前用户(很少变化)
const AuthContext = createContext<{ user: User | null; logout: () => void } | null>(null);

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

Context性能问题

// ❌ 问题:任何Context值的变化都会重新渲染所有消费者
const AppContext = createContext({
  user: null,
  cart: [],         // 频繁变化
  theme: 'light',   // 很少变化
  notifications: [], // 频繁变化
});

// 当购物车更新时,<ThemeToggle />即使不使用cart也会重新渲染!
function ThemeToggle() {
  const { theme, setTheme } = useContext(AppContext); // 订阅所有变化
  return <button onClick={() => setTheme('dark')}>{theme}</button>;
}

// ✅ 解决方案:按更新频率拆分Context
const ThemeContext = createContext(null);    // 很少变化
const UserContext = createContext(null);     // 很少变化
const CartContext = createContext(null);     // 经常变化
const NotificationContext = createContext(null); // 经常变化

Context适用于:主题、认证用户、本地化、功能开关——这些全局且不常变化的数据。

2026年React状态管理:Context vs Zustand vs Redux Toolkit 插图

Zustand — 最佳平衡点

Zustand简单、小巧,并解决了Context的重新渲染问题。

npm install zustand
// stores/cartStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

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

interface CartStore {
  items: CartItem[];
  addItem: (item: Omit<CartItem, 'quantity'>) => void;
  removeItem: (id: string) => void;
  updateQuantity: (id: string, quantity: number) => void;
  clearCart: () => void;
  total: () => number;
}

export const useCartStore = create<CartStore>()(
  persist(
    (set, get) => ({
      items: [],
      
      addItem: (newItem) => set((state) => {
        const existing = state.items.find(i => i.id === newItem.id);
        if (existing) {
          return {
            items: state.items.map(i => 
              i.id === newItem.id ? { ...i, quantity: i.quantity + 1 } : i
            )
          };
        }
        return { items: [...state.items, { ...newItem, quantity: 1 }] };
      }),
      
      removeItem: (id) => set(state => ({
        items: state.items.filter(i => i.id !== id)
      })),
      
      updateQuantity: (id, quantity) => set(state => ({
        items: quantity === 0 
          ? state.items.filter(i => i.id !== id)
          : state.items.map(i => i.id === id ? { ...i, quantity } : i)
      })),
      
      clearCart: () => set({ items: [] }),
      
      total: () => get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
    }),
    { name: 'cart-storage' } // 持久化到localStorage
  )
);
// 使用Zustand — 选择器防止不必要的重新渲染
function CartIcon() {
  // 仅在items.length变化时重新渲染
  const itemCount = useCartStore(state => state.items.length);
  return <span>{itemCount} items</span>;
}

function CartTotal() {
  // 仅在total变化时重新渲染
  const total = useCartStore(state => state.total());
  return <span>${total.toFixed(2)}</span>;
}

function AddToCartButton({ product }) {
  const addItem = useCartStore(state => state.addItem);
  return <button onClick={() => addItem(product)}>Add to Cart</button>;
}

Zustand与TypeScript和DevTools

import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

const useStore = create<Store>()(
  devtools(       // Chrome DevTools支持
    persist(      // localStorage持久化
      (set) => ({
        // ... store定义
      }),
      { name: 'my-store' }
    ),
    { name: 'MyStore' } // DevTools显示名称
  )
);

2026年React状态管理:Context vs Zustand vs Redux Toolkit 插图

Redux Toolkit — 适用于复杂应用

Redux Toolkit (RTK) 现代化了Redux,样板代码大大减少。

npm install @reduxjs/toolkit react-redux
// store/slices/userSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';

interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user';
}

interface UserState {
  currentUser: User | null;
  users: User[];
  loading: boolean;
  error: string | null;
}

// 异步thunk,自动生成pending/fulfilled/rejected动作
export const fetchUsers = createAsyncThunk(
  'users/fetchAll',
  async (_, { rejectWithValue }) => {
    try {
      const response = await fetch('/api/users');
      if (!response.ok) throw new Error('Failed to fetch');
      return await response.json();
    } catch (error) {
      return rejectWithValue(error.message);
    }
  }
);

const userSlice = createSlice({
  name: 'users',
  initialState: {
    currentUser: null,
    users: [],
    loading: false,
    error: null,
  } as UserState,
  
  reducers: {
    setCurrentUser: (state, action: PayloadAction<User>) => {
      state.currentUser = action.payload; // Immer允许“可变”写法
    },
    logout: (state) => {
      state.currentUser = null;
    },
    updateUser: (state, action: PayloadAction<Partial<User> & { id: string }>) => {
      const index = state.users.findIndex(u => u.id === action.payload.id);
      if (index !== -1) {
        state.users[index] = { ...state.users[index], ...action.payload };
      }
    },
  },
  
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.loading = false;
        state.users = action.payload;
      })
      .addCase(fetchUsers.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload as string;
      });
  },
});

export const { setCurrentUser, logout, updateUser } = userSlice.actions;
export default userSlice.reducer;
// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './slices/userSlice';
import cartReducer from './slices/cartSlice';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';

export const store = configureStore({
  reducer: {
    users: userReducer,
    cart: cartReducer,
  },
  // RTK自动包含redux-thunk和Immer
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

// 类型化的hooks(使用这些代替原始hooks)
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

RTK Query — 内置数据获取

// store/api/usersApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const usersApi = createApi({
  reducerPath: 'usersApi',
  baseQuery: fetchBaseQuery({ 
    baseUrl: '/api',
    prepareHeaders: (headers, { getState }) => {
      const token = (getState() as RootState).auth.token;
      if (token) headers.set('authorization', `Bearer ${token}`);
      return headers;
    },
  }),
  tagTypes: ['User'],
  endpoints: (builder) => ({
    getUsers: builder.query<User[], void>({
      query: () => '/users',
      providesTags: ['User'],
    }),
    getUserById: builder.query<User, string>({
      query: (id) => `/users/${id}`,
      providesTags: (result, error, id) => [{ type: 'User', id }],
    }),
    updateUser: builder.mutation<User, Partial<User> & { id: string }>({
      query: ({ id, ...patch }) => ({ url: `/users/${id}`, method: 'PATCH', body: patch }),
      invalidatesTags: (result, error, { id }) => [{ type: 'User', id }],
    }),
  }),
});

export const { useGetUsersQuery, useGetUserByIdQuery, useUpdateUserMutation } = usersApi;

// 使用
function UserList() {
  const { data: users, isLoading, error } = useGetUsersQuery();
  // 自动缓存、后台重新获取、加载状态!
}

决策指南

是本地组件状态吗?
  → useState / useReducer

是服务端/异步数据(API响应)吗?
  → TanStack Query 或 RTK Query(不是Zustand/Redux)

是共享的全局状态吗?
  → 小型/中型应用:Zustand
  → 大型应用且流程复杂:Redux Toolkit
  
是很少变化的UI状态吗?(主题、认证、本地化)
  → React Context

总结

2026年:

  • Context:主题、认证、本地化。简单、内置、免费。避免用于频繁更新的状态。
  • Zustand:大多数应用的最佳平衡点。小巧、简单、快速、开发体验好。
  • Redux Toolkit:大型应用、复杂异步流程、需要时间旅行调试。
  • TanStack Query/RTK Query:服务端状态。使用它们代替将API响应放入Redux。

→ 使用 JSON Viewer 检查和格式化你的状态快照。