【前端进阶】React状态管理完全指南:从useState到Redux

【前端进阶】React状态管理完全指南:从useState到Redux

引言

React作为Facebook开源的前端框架,已经成为现代Web应用开发的主流选择之一。在React应用中,状态管理是核心中的核心,无论是组件内部的状态、跨组件共享的状态,还是服务端同步的状态,都需要精心设计和管理。本文将全面深入地讲解React状态管理的各种方案,从最基本的useState到成熟的Redux架构,帮助读者构建可维护、可扩展的React应用。

一、React状态管理基础

1.1 什么是状态

在React中,状态(State)是指影响组件渲染的数据。与普通JavaScript变量不同,React状态具有响应式特性------当状态发生变化时,组件会自动重新渲染。状态可以分为以下几类:

  • UI状态:如弹窗开关、折叠面板、输入框值等
  • 业务数据状态:如用户信息、列表数据、缓存数据等
  • 服务器状态:如API返回的数据、加载状态、错误信息等
  • URL状态:如路由参数、查询字符串等

1.2 状态管理的演进

React状态管理方案经历了从简单到复杂的演进过程:

  1. 组件本地状态:使用useState/useReducer

  2. ** Props Drilling**:通过props层层传递

  3. Context API:跨层级状态共享

  4. 第三方状态库:Redux、MobX、Zustand等

    ┌─────────────────────────────────────────────────────────────┐
    │ React State Management │
    ├─────────────────────────────────────────────────────────────┤
    │ │
    │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
    │ │ useState │ │ useReducer │ │ useContext │ │
    │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
    │ │ │ │ │
    │ └───────────────────┼───────────────────┘ │
    │ │ │
    │ ┌──────────▼──────────┐ │
    │ │ Context │ │
    │ └──────────┬──────────┘ │
    │ │ │
    │ ┌────────────────────┼────────────────────┐ │
    │ │ │ │ │
    │ ┌──────▼──────┐ ┌───────▼───────┐ ┌──────▼──────┐ │
    │ │ Redux │ │ MobX │ │ Zustand │ │
    │ └─────────────┘ └───────────────┘ └─────────────┘ │
    │ │
    └─────────────────────────────────────────────────────────────┘

二、useState与useReducer

2.1 useState最佳实践

jsx 复制代码
// 基础useState使用
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

// 复杂状态 - 对象形式
function UserProfile() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: 0,
    preferences: {
      theme: 'light',
      language: 'zh-CN',
      notifications: true
    }
  });
  
  // 局部更新user的某个字段
  const updateName = (name) => {
    setUser(prev => ({ ...prev, name }));
  };
  
  // 深层更新嵌套对象
  const updatePreference = (key, value) => {
    setUser(prev => ({
      ...prev,
      preferences: {
        ...prev.preferences,
        [key]: value
      }
    }));
  };
  
  return (
    <div>
      <input 
        value={user.name} 
        onChange={e => updateName(e.target.value)} 
      />
      <select 
        value={user.preferences.theme}
        onChange={e => updatePreference('theme', e.target.value)}
      >
        <option value="light">Light</option>
        <option value="dark">Dark</option>
      </select>
    </div>
  );
}

2.2 useReducer处理复杂状态

jsx 复制代码
// useReducer - 适用于复杂状态逻辑
import React, { useReducer } from 'react';

// Action Types
const ActionTypes = {
  SET_TITLE: 'SET_TITLE',
  ADD_TODO: 'ADD_TODO',
  TOGGLE_TODO: 'TOGGLE_TODO',
  DELETE_TODO: 'DELETE_TODO',
  CLEAR_COMPLETED: 'CLEAR_COMPLETED',
  SET_FILTER: 'SET_FILTER'
};

// Initial State
const initialState = {
  todos: [],
  filter: 'all', // 'all', 'active', 'completed'
  newTodoTitle: ''
};

// Reducer Function
function todoReducer(state, action) {
  switch (action.type) {
    case ActionTypes.SET_TITLE:
      return { ...state, newTodoTitle: action.payload };
    
    case ActionTypes.ADD_TODO:
      if (!state.newTodoTitle.trim()) return state;
      const newTodo = {
        id: Date.now(),
        title: state.newTodoTitle,
        completed: false,
        createdAt: new Date().toISOString()
      };
      return {
        ...state,
        todos: [...state.todos, newTodo],
        newTodoTitle: ''
      };
    
    case ActionTypes.TOGGLE_TODO:
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload
            ? { ...todo, completed: !todo.completed }
            : todo
        )
      };
    
    case ActionTypes.DELETE_TODO:
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload)
      };
    
    case ActionTypes.CLEAR_COMPLETED:
      return {
        ...state,
        todos: state.todos.filter(todo => !todo.completed)
      };
    
    case ActionTypes.SET_FILTER:
      return { ...state, filter: action.payload };
    
    default:
      return state;
  }
}

// 使用Reducer的组件
function TodoApp() {
  const [state, dispatch] = useReducer(todoReducer, initialState);
  
  // 筛选逻辑
  const filteredTodos = state.todos.filter(todo => {
    if (state.filter === 'active') return !todo.completed;
    if (state.filter === 'completed') return todo.completed;
    return true;
  });
  
  const activeCount = state.todos.filter(t => !t.completed).length;
  const completedCount = state.todos.length - activeCount;
  
  return (
    <div className="todo-app">
      <h1>Todo List</h1>
      
      <form onSubmit={e => {
        e.preventDefault();
        dispatch({ type: ActionTypes.ADD_TODO });
      }}>
        <input
          type="text"
          value={state.newTodoTitle}
          onChange={e => dispatch({
            type: ActionTypes.SET_TITLE,
            payload: e.target.value
          })}
          placeholder="What needs to be done?"
        />
        <button type="submit">Add</button>
      </form>
      
      <ul>
        {filteredTodos.map(todo => (
          <li key={todo.id} className={todo.completed ? 'completed' : ''}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => dispatch({
                type: ActionTypes.TOGGLE_TODO,
                payload: todo.id
              })}
            />
            <span>{todo.title}</span>
            <button onClick={() => dispatch({
              type: ActionTypes.DELETE_TODO,
              payload: todo.id
            })}>Delete</button>
          </li>
        ))}
      </ul>
      
      <div className="footer">
        <span>{activeCount} items left</span>
        
        <div className="filters">
          {['all', 'active', 'completed'].map(filter => (
            <button
              key={filter}
              className={state.filter === filter ? 'active' : ''}
              onClick={() => dispatch({
                type: ActionTypes.SET_FILTER,
                payload: filter
              })}
            >
              {filter}
            </button>
          ))}
        </div>
        
        {completedCount > 0 && (
          <button onClick={() => dispatch({
            type: ActionTypes.CLEAR_COMPLETED
          })}>
            Clear completed
          </button>
        )}
      </div>
    </div>
  );
}

2.3 useState vs useReducer选择

javascript 复制代码
// 选择建议

/*
 * useState 适用场景:
 * - 状态类型简单(字符串、数字、布尔值)
 * - 状态之间相互独立
 * - 组件内状态,不跨组件共享
 * - 状态更新逻辑简单
 */

// ✅ useState 合适的例子
function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [rememberMe, setRememberMe] = useState(false);
  // ...
}

/*
 * useReducer 适用场景:
 * - 复杂的状态对象
 * - 多个状态之间存在关联
 * - 状态更新逻辑复杂(涉及多个子值或嵌套更新)
 * - 需要实现类似"撤销/重做"的功能
 */

// ✅ useReducer 合适的例子
function ShoppingCart() {
  const [cart, dispatch] = useReducer(cartReducer, initialCart);
  // cart包含: items[], shippingAddress{}, paymentMethod{}, 
  //           couponCode, isCheckingOut, error...
  
  const addItem = (product) => dispatch({ type: 'ADD_ITEM', product });
  const removeItem = (itemId) => dispatch({ type: 'REMOVE_ITEM', itemId });
  const updateQuantity = (itemId, qty) => 
    dispatch({ type: 'UPDATE_QUANTITY', itemId, quantity: qty });
  const applyCoupon = (code) => 
    dispatch({ type: 'APPLY_COUPON', code });
  const checkout = () => dispatch({ type: 'CHECKOUT' });
  // ...
}

三、Context API深度使用

3.1 Context基础

jsx 复制代码
// 创建Context
import React, { createContext, useContext, useState } from 'react';

// 创建Theme Context
const ThemeContext = createContext(undefined);

// Provider组件
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const [primaryColor, setPrimaryColor] = useState('#1890ff');
  
  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
    setPrimaryColor(prev => prev === '#1890ff' ? '#52c41a' : '#1890ff');
  };
  
  const value = {
    theme,
    primaryColor,
    toggleTheme,
    isDark: theme === 'dark'
  };
  
  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}

// 自定义Hook - 方便消费
function useTheme() {
  const context = useContext(ThemeContext);
  if (context === undefined) {
    throw new Error('useTheme must be used within a ThemeProvider');
  }
  return context;
}

// 消费Context
function ThemeSwitch() {
  const { theme, toggleTheme } = useTheme();
  
  return (
    <button onClick={toggleTheme}>
      Current: {theme}, Click to toggle
    </button>
  );
}

function ThemedButton() {
  const { primaryColor } = useTheme();
  
  return (
    <button style={{ backgroundColor: primaryColor }}>
      Themed Button
    </button>
  );
}

3.2 复合组件与Context

jsx 复制代码
// 使用Context实现表单复合组件
import React, { createContext, useContext, useReducer } from 'react';

// Form Context
const FormContext = createContext(undefined);

// Form State Reducer
function formReducer(state, action) {
  switch (action.type) {
    case 'UPDATE_FIELD':
      return {
        ...state,
        values: {
          ...state.values,
          [action.field]: action.value
        },
        errors: {
          ...state.errors,
          [action.field]: undefined
        }
      };
    case 'SET_ERROR':
      return {
        ...state,
        errors: {
          ...state.errors,
          [action.field]: action.error
        }
      };
    case 'SET_SUBMITTING':
      return { ...state, isSubmitting: action.value };
    case 'RESET':
      return action.initialState;
    default:
      return state;
  }
}

// Form Provider
function Form({ children, initialValues, onSubmit, validate }) {
  const [state, dispatch] = useReducer(formReducer, {
    values: initialValues,
    errors: {},
    isSubmitting: false
  });
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    
    // 验证
    if (validate) {
      const errors = validate(state.values);
      if (Object.keys(errors).length > 0) {
        Object.entries(errors).forEach(([field, error]) => {
          dispatch({ type: 'SET_ERROR', field, error });
        });
        return;
      }
    }
    
    dispatch({ type: 'SET_SUBMITTING', value: true });
    
    try {
      await onSubmit(state.values);
    } catch (error) {
      console.error('Form submission error:', error);
    } finally {
      dispatch({ type: 'SET_SUBMITTING', value: false });
    }
  };
  
  const updateField = (field, value) => {
    dispatch({ type: 'UPDATE_FIELD', field, value });
  };
  
  const value = { state, updateField, dispatch };
  
  return (
    <FormContext.Provider value={value}>
      <form onSubmit={handleSubmit}>{children}</form>
    </FormContext.Provider>
  );
}

// 自定义Hook
function useFormField(name) {
  const { state, updateField } = useContext(FormContext);
  
  return {
    name,
    value: state.values[name] || '',
    error: state.errors[name],
    onChange: (e) => updateField(name, e.target.value)
  };
}

// 复合组件
function Input({ label, type = 'text', placeholder, required }) {
  const { state } = useContext(FormContext);
  // 实现...
}

function FormField({ name, label, required }) {
  const { state, updateField } = useContext(FormContext);
  const value = state.values[name] || '';
  const error = state.errors[name];
  
  return (
    <div className={`form-field ${error ? 'has-error' : ''}`}>
      <label>{label}</label>
      <input
        name={name}
        value={value}
        onChange={e => updateField(name, e.target.value)}
      />
      {error && <span className="error">{error}</span>}
    </div>
  );
}

// 使用示例
function RegistrationForm() {
  const handleSubmit = async (values) => {
    await fetch('/api/register', {
      method: 'POST',
      body: JSON.stringify(values)
    });
    // 处理成功
  };
  
  const validate = (values) => {
    const errors = {};
    if (!values.email) {
      errors.email = 'Email is required';
    } else if (!/\S+@\S+\.\S+/.test(values.email)) {
      errors.email = 'Invalid email format';
    }
    if (!values.password) {
      errors.password = 'Password is required';
    } else if (values.password.length < 8) {
      errors.password = 'Password must be at least 8 characters';
    }
    return errors;
  };
  
  return (
    <Form 
      initialValues={{ email: '', password: '', username: '' }}
      onSubmit={handleSubmit}
      validate={validate}
    >
      <FormField name="email" label="Email" required />
      <FormField name="password" label="Password" required />
      <FormField name="username" label="Username" />
      <button type="submit">Register</button>
    </Form>
  );
}

3.3 Context性能优化

jsx 复制代码
// Context性能优化 - 分离读写
import React, { createContext, useContext, useState, useMemo } from 'react';

// 创建两个独立的Context
const UserContext = createContext(undefined);
const UserUpdateContext = createContext(undefined);

// Provider组合
function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);
  
  const login = async (credentials) => {
    setLoading(true);
    try {
      const response = await fetch('/api/login', {
        method: 'POST',
        body: JSON.stringify(credentials)
      });
      const userData = await response.json();
      setUser(userData);
    } finally {
      setLoading(false);
    }
  };
  
  const logout = () => {
    setUser(null);
  };
  
  const updateProfile = async (updates) => {
    const response = await fetch('/api/profile', {
      method: 'PUT',
      body: JSON.stringify(updates)
    });
    const updatedUser = await response.json();
    setUser(updatedUser);
  };
  
  // 使用useMemo缓存value,避免不必要的重渲染
  const userValue = useMemo(() => ({
    user,
    loading,
    isAuthenticated: !!user
  }), [user, loading]);
  
  const updateValue = useMemo(() => ({
    login,
    logout,
    updateProfile
  }), []);
  
  return (
    <UserContext.Provider value={userValue}>
      <UserUpdateContext.Provider value={updateValue}>
        {children}
      </UserUpdateContext.Provider>
    </UserContext.Provider>
  );
}

// 分离消费 - 只订阅需要的内容
function useUser() {
  return useContext(UserContext);
}

function useUserUpdate() {
  return useContext(UserUpdateContext);
}

// 组件中使用
function UserProfile() {
  const { user, isAuthenticated } = useUser();
  // 只有user变化时重渲染
  
  if (!isAuthenticated) return <LoginPrompt />;
  return <div>{user.name}</div>;
}

function LoginButton() {
  const { login, logout } = useUserUpdate();
  const { isAuthenticated } = useUser();
  // 只有login/logout变化时重渲染
  
  return (
    <button onClick={isAuthenticated ? logout : login}>
      {isAuthenticated ? 'Logout' : 'Login'}
    </button>
  );
}

四、Redux状态管理

4.1 Redux核心概念

javascript 复制代码
// Redux核心概念图解
/*
 * Redux数据流:
 * 
 *   ┌─────────────────────────────────────────────────────┐
 *   │                     UI Layer                        │
 *   │  ┌─────────┐    ┌─────────┐    ┌─────────┐         │
 *   │  │Component│    │Component│    │Component│         │
 *   │  └────┬────┘    └────┬────┘    └────┬────┘         │
 *   │       │              │              │               │
 *   │       └──────────────┼──────────────┘               │
 *   │                      │                              │
 *   └──────────────────────┼──────────────────────────────┘
 *                          │ dispatch(action)
 *                          ▼
 *   ┌──────────────────────────────────────────────────────┐
 *   │                   Action                             │
 *   │  { type: 'ADD_TODO', payload: { text: 'Learn Redux' } }│
 *   └──────────────────────┬──────────────────────────────┘
 *                          │
 *                          ▼
 *   ┌──────────────────────────────────────────────────────┐
 *   │                   Reducer                            │
 *   │                                                     │
 *   │  (state, action) => newState                         │
 *   │                                                     │
 *   │  ┌─────────────────────────────────────────────┐    │
 *   │  │ Root Reducer                                │    │
 *   │  │  ┌─────────┐  ┌─────────┐  ┌─────────┐     │    │
 *   │  │  │  todos  │  │filters  │  │  users  │     │    │
 *   │  │  └─────────┘  └─────────┘  └─────────┘     │    │
 *   │  │      │            │            │           │    │
 *   │  │      └────────────┼────────────┘           │    │
 *   │  │                   ▼                         │    │
 *   │  │           combineReducers()                 │    │
 *   │  └─────────────────────────────────────────────┘    │
 *   └──────────────────────┬──────────────────────────────┘
 *                          │
 *                          ▼
 *   ┌──────────────────────────────────────────────────────┐
 *   │                   Store                              │
 *   │  ┌─────────────────────────────────────────────┐    │
 *   │  │  getState()                                 │    │
 *   │  │  dispatch(action)                           │    │
 *   │  │  subscribe(listener)                       │    │
 *   │  └─────────────────────────────────────────────┘    │
 *   └──────────────────────────────────────────────────────┘
 */

4.2 Redux Toolkit现代化使用

javascript 复制代码
// Redux Toolkit - slice示例
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { fetchUserAPI } from './api';

// 异步thunk
export const fetchUser = createAsyncThunk(
  'user/fetchUser',
  async (userId, { rejectWithValue }) => {
    try {
      const response = await fetchUserAPI(userId);
      return response.data;
    } catch (error) {
      return rejectWithValue(error.message);
    }
  }
);

// User Slice
const userSlice = createSlice({
  name: 'user',
  initialState: {
    currentUser: null,
    isLoading: false,
    error: null
  },
  reducers: {
    // 同步action
    updateUser: (state, action) => {
      state.currentUser = { ...state.currentUser, ...action.payload };
    },
    clearUser: (state) => {
      state.currentUser = null;
      state.error = null;
    }
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => {
        state.isLoading = true;
        state.error = null;
      })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.isLoading = false;
        state.currentUser = action.payload;
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.isLoading = false;
        state.error = action.payload || 'Failed to fetch user';
      });
  }
});

export const { updateUser, clearUser } = userSlice.actions;
export default userSlice.reducer;

// Selectors
export const selectCurrentUser = (state) => state.user.currentUser;
export const selectUserLoading = (state) => state.user.isLoading;
export const selectUserError = (state) => state.user.error;

4.3 Store配置与中间件

javascript 复制代码
// Redux Store配置
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
import productReducer from './productSlice';
import cartReducer from './cartSlice';
import { loggerMiddleware } from './middleware/logger';
import { apiMiddleware } from './middleware/api';

// 创建store
export const store = configureStore({
  reducer: {
    user: userReducer,
    products: productReducer,
    cart: cartReducer
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        // 忽略这些action路径
        ignoredActions: ['user/fetchUser/fulfilled'],
        // 忽略这些paths
        ignoredPaths: ['user.currentUser.metadata.lastLogin']
      },
      thunk: true
    }).concat(loggerMiddleware, apiMiddleware),
  devTools: process.env.NODE_ENV !== 'production',
  preloadedState: {}
});

// Store类型
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

// 自定义中间件示例
const loggerMiddleware = (storeAPI) => (next) => (action) => {
  console.group(action.type);
  console.log('Current state:', storeAPI.getState());
  console.log('Action:', action);
  const result = next(action);
  console.log('Next state:', storeAPI.getState());
  console.groupEnd();
  return result;
};

const apiMiddleware = (storeAPI) => (next) => (action) => {
  if (action.type === 'api/call') {
    const { endpoint, method, body, onSuccess, onError } = action.payload;
    
    fetch(endpoint, { method, body })
      .then(response => response.json())
      .then(data => storeAPI.dispatch(onSuccess(data)))
      .catch(error => storeAPI.dispatch(onError(error)));
  }
  return next(action);
};

4.4 在React中使用Redux

jsx 复制代码
// React-Redux连接组件
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { 
  selectCartItems, 
  selectCartTotal,
  selectCartItemCount,
  selectCartLoading 
} from './cartSlice';
import { 
  addToCart, 
  removeFromCart, 
  updateQuantity,
  clearCart 
} from './cartSlice';

// Hooks方式 (推荐)
function Cart() {
  const dispatch = useDispatch();
  const items = useSelector(selectCartItems);
  const total = useSelector(selectCartTotal);
  const itemCount = useSelector(selectCartItemCount);
  const isLoading = useSelector(selectCartLoading);
  
  const handleAddToCart = (product) => {
    dispatch(addToCart(product));
  };
  
  const handleRemoveItem = (itemId) => {
    dispatch(removeFromCart(itemId));
  };
  
  const handleUpdateQuantity = (itemId, quantity) => {
    dispatch(updateQuantity({ itemId, quantity }));
  };
  
  if (isLoading) return <div>Loading...</div>;
  
  return (
    <div className="cart">
      <h2>Shopping Cart ({itemCount} items)</h2>
      
      <div className="cart-items">
        {items.map(item => (
          <div key={item.id} className="cart-item">
            <img src={item.image} alt={item.name} />
            <div className="item-details">
              <h3>{item.name}</h3>
              <p>${item.price}</p>
            </div>
            <div className="quantity-controls">
              <button 
                onClick={() => handleUpdateQuantity(item.id, item.quantity - 1)}
                disabled={item.quantity <= 1}
              >-</button>
              <span>{item.quantity}</span>
              <button 
                onClick={() => handleUpdateQuantity(item.id, item.quantity + 1)}
              >+</button>
            </div>
            <button 
              className="remove-btn"
              onClick={() => handleRemoveItem(item.id)}
            >Remove</button>
          </div>
        ))}
      </div>
      
      <div className="cart-summary">
        <div className="total">
          <span>Total:</span>
          <span>${total.toFixed(2)}</span>
        </div>
        <button className="checkout-btn">
          Proceed to Checkout
        </button>
      </div>
    </div>
  );
}

// 选择器工厂函数 - 带参数
function createStructuredSelector(selectors) {
  return (state) => {
    const result = {};
    for (const key in selectors) {
      result[key] = selectors[key](state);
    }
    return result;
  };
}

// 使用示例
function ProductList({ categoryId }) {
  const { products, isLoading, error } = useSelector(
    createStructuredSelector({
      products: (state) => state.products.items.filter(
        p => p.categoryId === categoryId
      ),
      isLoading: (state) => state.products.isLoading,
      error: (state) => state.products.error
    })
  );
  
  // ...
}

五、Redux高级模式

5.1 RTK Query服务端状态管理

javascript 复制代码
// RTK Query - API数据获取
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ 
    baseUrl: '/api',
    prepareHeaders: (headers, { getState }) => {
      const token = getState().auth.token;
      if (token) {
        headers.set('authorization', `Bearer ${token}`);
      }
      return headers;
    }
  }),
  tagTypes: ['Product', 'User', 'Order'],
  endpoints: (builder) => ({
    // 获取产品列表
    getProducts: builder.query({
      query: ({ page = 1, limit = 20, category }) => ({
        url: '/products',
        params: { page, limit, category }
      }),
      providesTags: (result) => {
        if (result?.data) {
          return [
            { type: 'Product', id: 'LIST' },
            ...result.data.map(({ id }) => ({ type: 'Product', id }))
          ];
        }
        return [{ type: 'Product', id: 'LIST' }];
      }
    }),
    
    // 获取单个产品
    getProduct: builder.query({
      query: (id) => `/products/${id}`,
      providesTags: (result, error, id) => [{ type: 'Product', id }]
    }),
    
    // 创建产品
    createProduct: builder.mutation({
      query: (product) => ({
        url: '/products',
        method: 'POST',
        body: product
      }),
      invalidatesTags: [{ type: 'Product', id: 'LIST' }]
    }),
    
    // 更新产品
    updateProduct: builder.mutation({
      query: ({ id, ...product }) => ({
        url: `/products/${id}`,
        method: 'PATCH',
        body: product
      }),
      invalidatesTags: (result, error, { id }) => [
        { type: 'Product', id },
        { type: 'Product', id: 'LIST' }
      ]
    }),
    
    // 删除产品
    deleteProduct: builder.mutation({
      query: (id) => ({
        url: `/products/${id}`,
        method: 'DELETE'
      }),
      invalidatesTags: (result, error, id) => [{ type: 'Product', id }]
    })
  })
});

export const {
  useGetProductsQuery,
  useGetProductQuery,
  useCreateProductMutation,
  useUpdateProductMutation,
  useDeleteProductMutation
} = apiSlice;
jsx 复制代码
// 在组件中使用RTK Query
import React from 'react';
import { useGetProductsQuery, useUpdateProductMutation } from './apiSlice';

function ProductList() {
  const [page, setPage] = React.useState(1);
  const { 
    data, 
    isLoading, 
    isFetching, 
    isError, 
    error,
    refetch 
  } = useGetProductsQuery({ page, limit: 20 });
  
  const [updateProduct, { isLoading: isUpdating }] = useUpdateProductMutation();
  
  const handleUpdatePrice = async (productId, newPrice) => {
    try {
      await updateProduct({ id: productId, price: newPrice });
    } catch (error) {
      console.error('Update failed:', error);
    }
  };
  
  if (isLoading) return <div>Loading products...</div>;
  if (isError) return <div>Error: {error.message}</div>;
  
  return (
    <div>
      <button onClick={refetch} disabled={isFetching}>
        {isFetching ? 'Refreshing...' : 'Refresh'}
      </button>
      
      <div className="products">
        {data.products.map(product => (
          <div key={product.id} className="product-card">
            <h3>{product.name}</h3>
            <p>Price: ${product.price}</p>
            <p>Stock: {product.stock}</p>
            <button 
              onClick={() => handleUpdatePrice(product.id, product.price + 10)}
              disabled={isUpdating}
            >
              Increase Price
            </button>
          </div>
        ))}
      </div>
      
      <div className="pagination">
        <button 
          onClick={() => setPage(p => Math.max(1, p - 1))}
          disabled={page === 1}
        >
          Previous
        </button>
        <span>Page {page} of {data.totalPages}</span>
        <button 
          onClick={() => setPage(p => p + 1)}
          disabled={page >= data.totalPages}
        >
          Next
        </button>
      </div>
    </div>
  );
}

5.2 Redux订阅与性能

javascript 复制代码
// 细粒度订阅优化
import React from 'react';
import { useSelector, shallowEqual } from 'react-redux';

// 不好的做法 - 整个state变化就重渲染
function BadComponent() {
  const { user, products, orders } = useSelector(state => ({
    user: state.user,
    products: state.products,
    orders: state.orders
  }));
  // state任何部分变化都会触发重渲染
}

// 好的做法 - 只订阅需要的数据
function GoodComponent() {
  // 只订阅需要的字段
  const userName = useSelector(state => state.user.name);
  const productCount = useSelector(state => state.products.length);
  const recentOrders = useSelector(
    state => state.orders.slice(0, 5),
    shallowEqual  // 浅比较数组引用
  );
}

// 使用createSelector创建记忆化选择器
import { createSelector } from '@reduxjs/toolkit';

const selectCartItems = state => state.cart.items;
const selectCartItemsMetadata = createSelector(
  [selectCartItems],
  (items) => ({
    totalItems: items.reduce((sum, item) => sum + item.quantity, 0),
    uniqueItems: items.length,
    totalPrice: items.reduce((sum, item) => sum + item.price * item.quantity, 0)
  })
);

function CartSummary() {
  const { totalItems, uniqueItems, totalPrice } = useSelector(selectCartItemsMetadata);
  
  return (
    <div>
      <p>Total Items: {totalItems}</p>
      <p>Unique Items: {uniqueItems}</p>
      <p>Total Price: ${totalPrice.toFixed(2)}</p>
    </div>
  );
}

六、状态管理方案选择

6.1 方案对比

方案 适用场景 优点 缺点
useState 组件本地状态 简单、性能好 无法跨组件共享
useReducer 复杂组件状态 逻辑集中、易于测试 代码量较多
Context 跨组件传递 无需prop drilling 不适合频繁变化
Redux 全局状态 生态成熟、调试方便 学习曲线陡峭
MobX 响应式状态 简洁直观 魔法较多
Zustand 轻量状态 API简洁、性能好 生态较弱
RTK Query 服务端状态 自动缓存、同步 仅适于API数据

6.2 实际项目建议

javascript 复制代码
// 推荐的项目状态分层架构

/*
 * React项目状态管理架构:
 * 
 * ┌─────────────────────────────────────────────────────────────┐
 * │                    Server State Layer (RTK Query)            │
 * │  - API数据获取与缓存                                        │
 * │  - 自动重新获取、轮询                                       │
 * │  - 乐观更新                                                 │
 * └─────────────────────────────────────────────────────────────┘
 *                            │
 * ▼
 * ┌─────────────────────────────────────────────────────────────┐
 * │                    Global UI State (Zustand/Context)       │
 * │  - 主题、语言设置                                           │
 * │  - 侧边栏状态                                               │
 * │  - 模态框控制                                               │
 * └─────────────────────────────────────────────────────────────┘
 *                            │
 * ▼
 * ┌─────────────────────────────────────────────────────────────┐
 * │                    Domain State (Redux Toolkit)             │
 * │  - 用户认证状态                                             │
 * │  - 购物车                                                   │
 * │  - 复杂业务逻辑                                             │
 * └─────────────────────────────────────────────────────────────┘
 *                            │
 * ▼
 * ┌─────────────────────────────────────────────────────────────┐
 * │                    Local Component State (useState)         │
 * │  - 表单输入                                                 │
 * │  - UI交互状态                                               │
 * │  - 临时数据                                                 │
 * └─────────────────────────────────────────────────────────────┘
 */

// 具体项目示例
import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

// UI状态 - 使用Zustand
const useUIStore = create(
  devtools(
    persist(
      (set) => ({
        sidebarOpen: true,
        theme: 'light',
        language: 'zh-CN',
        toggleSidebar: () => set(state => ({ sidebarOpen: !state.sidebarOpen })),
        setTheme: (theme) => set({ theme }),
        setLanguage: (lang) => set({ language: lang })
      }),
      { name: 'ui-storage' }
    )
  )
);

// 购物车状态 - 使用Zustand(简单场景)
const useCartStore = create((set, get) => ({
  items: [],
  addItem: (product) => set(state => {
    const existing = state.items.find(i => i.id === product.id);
    if (existing) {
      return {
        items: state.items.map(i => 
          i.id === product.id 
            ? { ...i, quantity: i.quantity + 1 }
            : i
        )
      };
    }
    return { items: [...state.items, { ...product, quantity: 1 }] };
  }),
  removeItem: (id) => set(state => ({
    items: state.items.filter(i => i.id !== id)
  })),
  clearCart: () => set({ items: [] }),
  total: () => get().items.reduce((sum, i) => sum + i.price * i.quantity, 0)
}));

// 表单状态 - useState
function ContactForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [message, setMessage] = useState('');
  // ...
}

七、总结

React状态管理是一个复杂但至关重要的主题。本文从基础出发,系统介绍了useState、useReducer、Context API和Redux四种主要状态管理方案,深入讲解了它们的原理、使用方法和最佳实践。

在实际项目中,没有放之四海而皆准的最优方案,需要根据具体场景和团队情况选择合适的状态管理策略。建议:

  1. 优先使用简单方案:能用useState解决的就不要引入Redux
  2. 分层管理状态:按职责将状态分为不同层次
  3. 遵循不可变原则:确保状态更新的可预测性
  4. 重视性能优化:使用记忆化选择器避免不必要的重渲染
  5. 善用工具:Redux DevTools可以极大提升调试效率

希望本文能够帮助读者建立完整的React状态管理知识体系,在实际项目中做出正确的技术决策。

相关推荐
网宿安全演武实验室3 小时前
AI 赋能代码审计:静态扫描与AI Skill的协同实践
人工智能·主机安全·终端安全·网络攻防
hh.h.3 小时前
PyTorch模型适配昇腾NPU:从零开始的端到端流程
人工智能·pytorch·python·cann
老詹图解IT3 小时前
AI时代的个人隐私与网络安全自保——从账号密码到设备行为的完整体系
人工智能·安全·web安全
MediaTea3 小时前
DL:循环神经网络的基本原理与 PyTorch 实现
人工智能·pytorch·rnn·深度学习·神经网络
幸运的大号暖贴3 小时前
AI LED Light — 给你的 AI 编程助手做一个实体指示灯
人工智能
2601_957190903 小时前
迷拟极速飞车:多人同台竞速,轻量化高效落地
人工智能
徐安安ye3 小时前
FlashAttention的OOM排查:为什么显存够了还是报内存不足?
人工智能·算法·机器学习
灰灰勇闯IT3 小时前
AI Agent 推理:从单次对话到多轮工具调用
人工智能·microsoft
L、2183 小时前
CANN异构计算实践:CPU+NPU协同工作的最佳模式
网络·人工智能·pytorch·python·安全