0Pricing
Frontend Academy · 课时

使用 useReducer 管理复杂状态

使用 reducer 函数建模状态转换,分发操作对象,并在状态逻辑变得复杂时优先使用 useReducer 而不是 useState。

使用 useReducer 管理复杂状态 是 CoddyKit 上的免费 Frontend Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Frontend Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Frontend Academy 课程共包含 4 节课。

当 useState 不够用时

当状态转换包含复杂逻辑、多个相关值,或新状态以非简单方式依赖前一个状态时,相比多次调用 useState,useReducer 是更简洁的选择。

归约函数模式

归约函数是一个纯函数:(state, action) => newState。它接收当前状态和一个操作对象,并返回下一个状态。不要进行修改——始终返回一个新对象。

type Action =
  | { type: 'increment' }
  | { type: 'decrement' }
  | { type: 'reset'; payload: number };

function counterReducer(state: number, action: Action): number {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    case 'reset':     return action.payload;
    default: return state;
  }
}

useReducer 语法

useReducer(reducer, initialState) 会返回当前状态和一个 dispatch 函数。使用操作对象调用 dispatch,以触发状态转换。

function Counter() {
  const [count, dispatch] = useReducer(counterReducer, 0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
    </div>
  );
}

类型化操作——可辨识联合类型

对操作使用 TypeScript 可辨识联合类型,以便在归约函数的 switch 语句中获得完备的类型检查。

type CartAction =
  | { type: 'ADD_ITEM'; item: CartItem }
  | { type: 'REMOVE_ITEM'; id: string }
  | { type: 'SET_QUANTITY'; id: string; qty: number }
  | { type: 'CLEAR' };

复杂状态示例:购物车

需要执行添加、移除和更新数量操作的购物车,是使用 useReducer 的理想场景。

interface CartState {
  items: CartItem[];
  total: number;
}

function cartReducer(state: CartState, action: CartAction): CartState {
  switch (action.type) {
    case 'ADD_ITEM': {
      const exists = state.items.find(i => i.id === action.item.id);
      if (exists) {
        const items = state.items.map(i =>
          i.id === action.item.id ? { ...i, qty: i.qty + 1 } : i
        );
        return { ...state, items, total: calcTotal(items) };
      }
      const items = [...state.items, { ...action.item, qty: 1 }];
      return { ...state, items, total: calcTotal(items) };
    }
    case 'CLEAR': return { items: [], total: 0 };
    default: return state;
  }
}

分发函数是稳定的

dispatch 函数是稳定的——它在不同渲染之间不会改变。将它作为属性传递,或将它用作 useEffect 依赖项都是安全的,不会因此触发重复执行。

组合使用 useReducer 与 useContext

一种强大的模式是:让上下文同时提供状态和分发函数,使任何消费者都能读取状态或分发操作,而无需逐层传递属性。

const CartContext = createContext<{ state: CartState; dispatch: Dispatch<CartAction> } | null>(null);

function CartProvider({ children }: { children: React.ReactNode }) {
  const [state, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
  return <CartContext.Provider value={{ state, dispatch }}>{children}</CartContext.Provider>;
}

useReducer 与 useState

在以下情况下请选择 useReducer:状态包含多个子值;下一个状态以复杂方式依赖前一个状态;转换很多且需要明确命名;或者您希望将状态逻辑与组件分开测试。

单独测试归约函数

由于归约函数是纯函数,因此无需渲染任何组件就能轻松测试。

test('increment action increases count', () => {
  expect(counterReducer(0, { type: 'increment' })).toBe(1);
  expect(counterReducer(5, { type: 'increment' })).toBe(6);
});

test('reset action sets count to payload', () => {
  expect(counterReducer(42, { type: 'reset', payload: 0 })).toBe(0);
});

使用 Immer 进行不可变更新

immer 库允许您编写看起来像是在修改状态的代码,同时生成新的不可变状态。它适合在归约函数中更新复杂的嵌套对象。

快速检查

归约函数的函数签名是什么?

总结:useReducer

(state, action) => newState——纯函数,不进行修改。useReducer(reducer, initial) 返回 [state, dispatch]。使用可辨识联合类型的操作类型来提高安全性。分发函数是稳定的。它非常适合包含大量状态转换的复杂状态。将它与 useContext 结合,可进行全局状态管理。将归约函数作为纯函数单独测试。

常见问题解答

「使用 useReducer 管理复杂状态」课时是免费的吗?

是的 — 「使用 useReducer 管理复杂状态」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Frontend Academy 课程的其余内容,请升级到 CoddyKit PRO。 Frontend Academy 课程共包含 4 节课。

「使用 useReducer 管理复杂状态」这节课中我会学到什么?

使用 reducer 函数建模状态转换,分发操作对象,并在状态逻辑变得复杂时优先使用 useReducer 而不是 useState。 你通过在浏览器中直接运行的动手代码来练习 Frontend Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Frontend Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Frontend Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「使用 useReducer 管理复杂状态」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Frontend Academy 课中编写并运行代码吗?

能。每节 Frontend Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 useContext 管理全局状态
  2. 使用 useReducer 管理复杂状态
  3. 使用 useMemo 与 useCallback 提升性能
  4. 自定义 Hook:提取可复用逻辑
← 返回 Frontend Academy