5、React 状态管理进阶

4 阅读4分钟

太棒了!👏 你已经掌握了 React 的核心能力,现在是时候构建大型应用所需的状态管理架构了。


—— 用 Context + useReducer 构建全局状态

🎯 目标:解决“跨组件共享状态”问题,如:

  • 用户登录信息
  • 购物车
  • 主题设置
  • 多步骤表单数据

当你发现要一层层传递 props 或频繁使用回调时,就是时候升级状态管理了!


一、为什么需要更强大的状态管理?⚠️

❌ 常见痛点:

问题描述
Prop Drilling状态要穿过多个中间组件才能到达深层子组件
状态分散相关逻辑散落在多个组件中,难以维护
兄弟通信复杂必须通过父组件中转,代码臃肿

✅ 解决方案演进路径:

Props → Context → Context + useReducer → Redux / Zustand

我们先掌握 Context + useReducer —— 它是理解 Redux 的最佳跳板。


二、useReducer:管理复杂状态的 Hook 🧠

📌 类比:

  • useState:适合简单值(数字、字符串、布尔)
  • useReducer:适合复杂对象或有多种变化逻辑的状态

✅ 基本语法:

const [state, dispatch] = useReducer(reducer, initialState);
  • reducer:一个函数,接收 (state, action) 并返回新 state
  • dispatch:派发动作的方式,如 dispatch({ type: 'ADD_ITEM' })

💡 示例:购物车状态管理

1. 定义初始状态和 reducer

// cartStore.js
const initialState = {
  items: [],
  total: 0,
};

function cartReducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return {
        ...state,
        items: [...state.items, action.payload],
        total: state.total + action.payload.price,
      };

    case 'REMOVE_ITEM':
      const item = state.items.find(item => item.id === action.payload);
      return {
        ...state,
        items: state.items.filter(item => item.id !== action.payload),
        total: state.total - (item?.price || 0),
      };

    case 'CLEAR_CART':
      return initialState;

    default:
      return state;
  }
}

2. 使用 useReducer

import { useReducer } from 'react';
import { cartReducer, initialState } from './cartStore';

function ShoppingCart() {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  return (
    <div>
      <p>商品数量:{state.items.length}</p>
      <p>总价:¥{state.total}</p>
      <button onClick={() =>
        dispatch({
          type: 'ADD_ITEM',
          payload: { id: Date.now(), name: '商品', price: 99 }
        })
      }>
        添加商品
      </button>
      <button onClick={() => dispatch({ type: 'CLEAR_CART' })}>
        清空购物车
      </button>
    </div>
  );
}

✅ 关键优势:

  • 所有状态变更集中在 reducer
  • 易于调试、测试、追踪变化
  • 支持“时间旅行”思想(Redux 核心理念)

三、结合 Context:实现全局状态共享 🌐

现在让多个组件都能访问和修改这个购物车状态。

✅ 步骤 1:创建 Context

// CartContext.js
import { createContext } from 'react';

const CartContext = createContext();

export default CartContext;

✅ 步骤 2:创建 Provider 组件

// CartProvider.jsx
import { useReducer } from 'react';
import CartContext from './CartContext';
import { cartReducer, initialState } from './cartStore';

export function CartProvider({ children }) {
  const [state, dispatch] = useReducer(cartReducer, initialState);

  return (
    <CartContext.Provider value={{ state, dispatch }}>
      {children}
    </CartContext.Provider>
  );
}

✅ 步骤 3:在顶层包裹应用

// App.js
import { CartProvider } from './CartProvider';

function App() {
  return (
    <CartProvider>
      <Header />
      <Main />
      <Sidebar />
    </CartProvider>
  );
}

✅ 步骤 4:任意组件使用状态

// Header.js
import { useContext } from 'react';
import CartContext from './CartContext';

function Header() {
  const { state } = useContext(CartContext);

  return (
    <header>
      <h1>我的商城</h1>
      <span>购物车:{state.items.length} 件</span>
    </header>
  );
}

// ProductItem.js
import { useContext } from 'react';
import CartContext from './CartContext';

function ProductItem({ product }) {
  const { dispatch } = useContext(CartContext);

  return (
    <div>
      <h3>{product.name}</h3>
      <p>¥{product.price}</p>
      <button onClick={() =>
        dispatch({
          type: 'ADD_ITEM',
          payload: product
        })
      }>
        加入购物车
      </button>
    </div>
  );
}

四、优化建议与最佳实践 ✅

1. 拆分 reducer(可选)

当逻辑变多时,可用 combineReducers 模式:

const rootReducer = combineReducers({
  cart: cartReducer,
  user: userReducer,
  ui: uiReducer,
});

2. 封装 dispatch 逻辑

避免在组件中写复杂的 action:

// utils/cartActions.js
export const addToCart = (item) => ({
  type: 'ADD_ITEM',
  payload: item,
});

export const removeFromCart = (id) => ({
  type: 'REMOVE_ITEM',
  payload: id,
});

然后在组件中:

dispatch(addToCart(product));

3. 性能优化

  • 使用 React.memo 包裹不常变的组件
  • 避免在 value 中传太多数据
  • 可考虑将 dispatchstate 分开提供 Context(高级)

五、与 Redux 对比 🆚

特性Context + useReducerRedux
学习成本低(原生支持)中高
调试工具无内置 DevTools强大的 Redux DevTools
中间件支持需手动实现支持 thunk/saga 等
社区生态小而精庞大成熟
适用场景中小型项目大型复杂应用

✅ 建议路径:

  • 初级 → useState + props
  • 中级 → Context + useReducer
  • 高级 → Redux Toolkit 或 Zustand

六、实战练习 🏋️‍♀️

✅ 练习 1:用户登录状态管理

目标:创建一个全局登录状态系统

  1. 定义 authReducer
    • 初始状态:{ user: null, isLoggedIn: false }
    • Action:LOGIN, LOGOUT
  1. 创建 AuthContextAuthProvider
  2. LoginButton 组件中调用 dispatch({ type: 'LOGIN', payload: user })
  3. Profile 组件中显示用户名或“未登录”

✅ 练习 2:主题切换器(进阶)

  1. 状态包含:theme: 'light' | 'dark'
  2. Action:TOGGLE_THEME
  3. App 根节点根据 state.theme 添加 className
  4. 按钮点击触发切换

💡 提示:结合 useEffect 把主题保存到 localStorage


✅ 总结:状态管理进阶要点

概念要点
useReducer适合复杂状态,集中管理变化逻辑
Context提供全局可访问的数据通道
组合使用实现轻量级全局状态管理
Action 对象{ type, payload } 结构清晰
单向数据流State → View → Dispatch → Reducer → New State
为 Redux 铺路理解这套模式后,学 Redux 会非常轻松

🎯 下一步预告
你已经具备构建中大型应用的能力!接下来我们要进入现代化开发流程:

➡️ React + TypeScript:类型安全开发
—— 让你的代码更健壮、智能提示更强、团队协作更高效

是否继续?我将带你进入 第七课:TypeScript 与 React 的完美结合