React中,useState和useReducer有什么区别

210 阅读4分钟

在 React 中,useState 和 useReducer 都是用于管理组件状态的 Hook,但它们的设计理念、适用场景和使用方式有显著区别。理解这些区别可以帮助我们在不同场景下选择更合适的状态管理方案。

Taimili 艾米莉 ( 一款免费开源的 taimili.com )

艾米莉 是一款优雅便捷的 GitHub Star 管理和加星工具,基于 PHP & javascript 构建, 能对github 得 star fork follow watch 管理和提升,最适合github 的深度用户

image.png

核心区别对比

维度useStateuseReducer
设计理念轻量、简洁的状态管理,适合简单场景基于「Reducer 模式」,通过 action 驱动状态变更,适合复杂逻辑
状态复杂度适合管理单一值或简单结构(如布尔、数字、简单对象)适合管理多关联状态、复杂状态逻辑(如多字段表单、状态变更依赖多个条件)
更新逻辑直接通过 setState 函数更新状态通过 dispatch(action) 触发 reducer 函数计算新状态
逻辑复用复杂逻辑复用较困难reducer 函数可独立提取,便于复用和测试
调试友好度状态变更分散,难以追踪通过 action 类型可清晰追踪状态变更原因

1. 基本用法与设计思路

useState:简单状态的直接管理

useState 是最基础的状态管理 Hook,设计目标是简洁直观。它接收一个初始状态,返回一个「状态变量」和「更新函数」,通过直接调用更新函数修改状态。

jsx

// 示例:用 useState 管理计数器
function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>加 1</button>
      <button onClick={() => setCount(count - 1)}>减 1</button>
    </div>
  );
}

核心特点

  • 状态更新逻辑直接写在事件处理中(如 count + 1);
  • 适合状态变化简单、逻辑单一的场景。

useReducer:复杂状态的结构化管理

useReducer 借鉴了 Redux 的思想,通过「Reducer 函数」和「Action」来管理状态。它接收两个参数:

  • reducer:一个纯函数,接收当前状态和 action,返回新状态;

  • initialState:初始状态。

返回值为「当前状态」和「dispatch 函数」,通过 dispatch(action) 触发状态更新。

jsx

// 示例:用 useReducer 管理计数器
// 1. 定义 reducer 函数(纯函数,处理状态逻辑)
function countReducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    case 'RESET':
      return 0;
    default:
      return state;
  }
}

// 2. 在组件中使用
function Counter() {
  const [count, dispatch] = useReducer(countReducer, 0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>加 1</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>减 1</button>
      <button onClick={() => dispatch({ type: 'RESET' })}>重置</button>
    </div>
  );
}

核心特点

  • 状态更新逻辑集中在 reducer 中,通过 action.type 区分不同操作;
  • 状态变化的意图更清晰(如 INCREMENT 明确表示「增加」)。

2. 适用场景差异

优先用 useState 的场景:

  • 状态结构简单(如单个布尔值、数字、字符串);

  • 状态更新逻辑单一(如直接赋值、简单计算);

  • 组件规模小,状态变更少。

例如:开关状态(isOpen)、输入框内容(inputValue)、加载状态(isLoading)等。

优先用 useReducer 的场景:

  • 状态结构复杂(如包含多个子字段的对象,{ name, age, address });

  • 状态更新依赖多个条件或前一个状态(如「先判断再更新」的逻辑);

  • 存在多种状态变更方式(如一个表单有「提交」「重置」「修改单个字段」等操作);

  • 需要复用状态逻辑(reducer 可提取到组件外,供多个组件使用);

  • 调试需求高(通过 action 可追踪状态变更历史)。

例如:购物车(添加、删除、修改数量)、多步骤表单、复杂游戏状态等。

3. 其他关键区别

状态更新的依赖处理

useState 若要基于前一个状态更新,需使用函数式更新(避免闭包陷阱):

jsx

// 正确:基于前一个状态更新
setCount(prevCount => prevCount + 1);

useReducer 天然基于前一个状态计算新状态(reducer 的第一个参数就是当前状态),无需额外处理闭包问题:

jsx

// reducer 中直接使用当前状态
function reducer(state, action) {
  return { ...state, count: state.count + 1 }; // 安全依赖当前状态
}

逻辑复用能力

useReducer 的 reducer 是纯函数,可独立于组件存在,便于复用和测试:

jsx

// 提取到单独文件(如 reducers.js)
export function formReducer(state, action) { /* ... */ }

// 在多个组件中导入使用
import { formReducer } from './reducers';
function FormA() { const [state, dispatch] = useReducer(formReducer, initialA); }
function FormB() { const [state, dispatch] = useReducer(formReducer, initialB); }

而 useState 的更新逻辑若复杂,往往分散在组件的事件处理中,复用难度大。

总结

  • useState 是「简单状态的快捷方式」,适合逻辑简单、状态独立的场景,写法更简洁;

  • useReducer 是「复杂状态的结构化方案」,适合逻辑复杂、状态关联的场景,更利于维护和调试。

实际开发中,两者并非互斥关系:一个组件中可以同时使用 useState(管理简单状态)和 useReducer(管理复杂状态),根据具体需求灵活选择即可。