人人都会AI编程

13.2 原生方案:useState + useContext + useReducer

更新时间:2026-07-11

在引入任何第三方状态管理库之前,React 本身已经提供了一套完整的状态管理原语。通过组合 useStateuseContextuseReducer,你可以在不安装额外依赖的情况下,管理从简单到中等复杂度的应用状态。这套原生方案的最大优势是零额外依赖、学习成本低、与 React 深度集成

三个 API 的职责分工

  • useState:管理组件内部的局部状态,适合简单的、独立的状态值。
  • useContext:将状态从祖先组件传递到任意深度的子组件,打破 Props 层层传递的“下钻”问题。
  • useReducer:当状态更新逻辑变得复杂(多个子值、依赖前一状态),提供可预测的状态更新模式,类似 Redux 的 Reducer 概念。

三者组合使用,可以构成一个轻量级但功能完备的全局或模块级状态管理体系。

从 Props 下钻到 Context 提升

在没有 Context 的情况下,如果多个嵌套组件需要共享某个状态,必须通过 Props 一层层手动传递,称为 Props Drilling。这会让中间组件被迫接收并不关心的属性。

// ❌ Props 层层传递,中间组件耦合了无关状态
function GrandParent() {
  const [theme, setTheme] = useState('light');
  return <Parent theme={theme} onToggle={() => setTheme(t => t === 'light' ? 'dark' : 'light')} />;
}

function Parent({ theme, onToggle }) {
  return <Child theme={theme} onToggle={onToggle} />;
}

function Child({ theme, onToggle }) {
  return <div className={theme}><button onClick={onToggle}>切换</button></div>;
}

使用 createContextuseContext 可以优雅解决:

// ✅ 通过 Context 直接向深层组件注入状态
const ThemeContext = createContext();

function GrandParent() {
  const [theme, setTheme] = useState('light');
  const toggle = () => setTheme(t => t === 'light' ? 'dark' : 'light');
  return (
    <ThemeContext.Provider value={{ theme, toggle }}>
      <Parent />
    </ThemeContext.Provider>
  );
}

function Parent() {
  return <Child />;
}

function Child() {
  const { theme, toggle } = useContext(ThemeContext);
  return <div className={theme}><button onClick={toggle}>切换</button></div>;
}

Parent 完全无需关心 themeChild 直接从 Context 中获取所需数据。这是原生方案中实现跨层级共享的核心机制。

useReducer:管理复杂状态逻辑

当状态更新依赖于前一个状态,或者一个操作会同时修改多个状态值时,useState 的回调写法会很分散。useReducer 可以将所有更新逻辑集中到一个纯函数中,让状态变更可预测、可测试。

典型示例:管理一个计数器,以及它的历史记录。

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {
        ...state,
        count: state.count + 1,
        history: [...state.history, state.count + 1]
      };
    case 'decrement':
      return {
        ...state,
        count: state.count - 1,
        history: [...state.history, state.count - 1]
      };
    case 'reset':
      return { count: 0, history: [] };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0, history: [] });
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
      <p>History: {state.history.join(', ')}</p>
    </div>
  );
}

reducer 是一个纯函数,接收当前状态和描述“要做什么”的 action,返回新状态。这使得状态变更完全可预测,也便于编写单元测试。

combine:构建全局状态管理

useReduceruseContext 结合,可以获得一个类似 Redux 的全局状态管理方案,且零依赖。

步骤:

  1. 创建一个 Context。
  2. 在顶层组件中使用 useReducer 管理全局状态。
  3. 将状态和 dispatch 函数通过 Context 向下传递。
  4. 任何后代组件通过 useContext 读取状态、触发更新。

下面是一个购物车全局管理的简化示例:

// 定义 reducer 和 Context
const CartContext = createContext();

function cartReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, action.payload];
    case 'remove':
      return state.filter(item => item.id !== action.payload.id);
    case 'clear':
      return [];
    default:
      return state;
  }
}

// 顶层 Provider 组件
function CartProvider({ children }) {
  const [cart, dispatch] = useReducer(cartReducer, []);

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

// 自定义 Hook,方便消费
function useCart() {
  const context = useContext(CartContext);
  if (!context) {
    throw new Error('useCart must be used within a CartProvider');
  }
  return context;
}

任何组件只需调用 useCart() 即可访问购物车数据或派发更新:

function Product({ product }) {
  const { dispatch } = useCart();
  const addToCart = () => dispatch({ type: 'add', payload: product });
  return <button onClick={addToCart}>加入购物车</button>;
}

function CartSummary() {
  const { cart } = useCart();
  return <p>购物车中共有 {cart.length} 件商品</p>;
}

原生方案的适用场景与局限

适用场景:

  • 小型到中型应用,全局状态结构简单。
  • 模块级别的共享状态(如多步骤表单、购物车),不必要引入全局 Store。
  • 团队不想引入额外库,追求最小化依赖。

局限性:

  • 当应用规模增大,Context 的频繁更新会导致所有消费此 Context 的组件重新渲染,即使它们只用到了状态的一部分。需要手动使用 useMemo、拆分 Context 来优化。
  • 缺少中间件、类似 Redux DevTools、时间旅行调试等高级特性。
  • 状态逻辑较复杂时,reducer 会逐渐膨胀,不如 Zustand 等方案原子化。

性能优化:拆分 Context 避免不必要渲染

许多开发者错误地将所有全局状态扔进一个巨大的 Context,导致一个状态变动触发所有消费者重渲染。正确的做法是按职责拆分 Context

// 按领域拆分成两个 Context
const AuthContext = createContext();
const CartContext = createContext();

function AppProvider({ children }) {
  const [user, setUser] = useState(null);
  const [cart, dispatch] = useReducer(cartReducer, []);

  return (
    <AuthContext.Provider value={{ user, setUser }}>
      <CartContext.Provider value={{ cart, dispatch }}>
        {children}
      </CartContext.Provider>
    </AuthContext.Provider>
  );
}

这样只有当 user 变化时,AuthContext 的消费者才重渲染;只有 cart 变化时,CartContext 的消费者才受影响。这是原生方案规避性能问题的关键手段。

总结

useState + useContext + useReducer 构成了 React 的原生状态管理铁三角。它无需任何第三方库即可实现从局部到全局的状态共享,是理解所有状态管理方案的基础。当应用复杂度持续上升,出现频繁的全局状态变动和性能瓶颈时,再考虑引入 Zustand、Redux Toolkit 等更专业的方案。但无论用哪一款,核心原理都与这套原生模式一脉相承。