南屹川头像
关注

【前端进阶】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最佳实践

// 基础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处理复杂状态

// 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选择

// 选择建议

/*
 * 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基础

// 创建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

// 使用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性能优化

// 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核心概念

// 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现代化使用

// 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配置与中间件

// 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

// 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服务端状态管理

// 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;
// 在组件中使用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订阅与性能

// 细粒度订阅优化
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 实际项目建议

// 推荐的项目状态分层架构

/*
 * 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状态管理知识体系,在实际项目中做出正确的技术决策。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/weixin_43272162/article/details/161348440

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--