无废话之 useState、useRef、useReducer 的使用场景与选择指南

在 React 中,最常用的状态管理 Hook 有三个:

  • useState
  • useRef
  • useReducer

它们都能"存数据",但作用完全不同。

本文通过对比、代码示例和最佳实践,让你一眼看懂三者的异同点与使用策略。

1. 三者一句话总结(记住这个就够了)

Hook 特点 什么时候用
useState 会触发组件重新渲染 UI 需要根据数据变化而更新
useRef 不会触发渲染,可持久存储 保存 DOM、保存不影响 UI 的数据、避免频繁渲染
useReducer 适合复杂状态逻辑,集中管理 多步骤状态、复杂更新规则、类似 Vuex/Redux

2. useState ------ 最常用的 UI 状态管理方式

📌 用途

  • 管理与 UI 显示相关的状态
  • 一旦更新 → React 会重新渲染组件

📌 示例:计数器

jsx 复制代码
const [count, setCount] = useState(0);

return (
  <button onClick={() => setCount(count + 1)}>
    {count}
  </button>
);

👉 每次 setCount 运行,UI 都会更新。

3. useRef ------ 不触发渲染的"可变容器"

📌 用途

  • 保存不会影响 UI 的值(计时器、缓存、临时变量)
  • 保存 DOM 节点引用
  • 在渲染周期之间持久化数据

📌 示例:保存一个不会影响 UI 的计数器

jsx 复制代码
const counterRef = useRef(0);

function add() {
  counterRef.current += 1;
  console.log(counterRef.current);
}

return <button onClick={add}>Add</button>;

👉 按多少次,UI 都不会变化,因为它 不会触发重渲染

4. useReducer ------ 多分支、复杂逻辑的状态管理

📌 用途

适合以下情况:

  • 状态结构复杂(多字段)
  • 更新逻辑复杂(多 if/else 或 switch)
  • 想将逻辑分离,让代码更清晰

📌 示例:管理一个表单对象

jsx 复制代码
function reducer(state, action) {
  switch (action.type) {
    case "setName":
      return { ...state, name: action.payload };
    case "setAge":
      return { ...state, age: action.payload };
    case "reset":
      return { name: "", age: 0 };
    default:
      return state;
  }
}

const [form, dispatch] = useReducer(reducer, {
  name: "",
  age: 0,
});

👉 适合"动作驱动"的状态结构。

5. 三者的核心区别(最关键)

对比点 useState useRef useReducer
是否触发渲染 ✔ 会 ❌ 不会 ✔ 会
存储数据类型 简单/基本 任意 复杂对象
逻辑复杂度 简单 简单 中-高
适合多字段状态 不太合适 不合适 ✔ 最合适
跨 render 保留值
管理 DOM
适合封装业务逻辑 一般 ✔ 非常好

6. 如何选择?(最实用的决策树)

🟦 1)数据是否影响 UI?

  • 是 → useState 或 useReducer
  • 否 → useRef

🟩 2)数据更新逻辑是否复杂?

  • 复杂(多字段、多动作)→ useReducer
  • 简单(一个值)→ useState

🟨 3)更新是否非常频繁?(例如输入法、mousemove)

  • 是,但 UI 不依赖 → useRef
  • 是,且 UI 要更新 → useState + 性能优化

🟧 4)是否需要类似 Redux 的写法?

  • 是 → useReducer
  • 否 → useState / useRef

7. 最容易犯的错误(务必注意)

把 useRef 当 useState 用

jsx 复制代码
const count = useRef(0);
count.current++; // UI 不更新!

👉 你以为 UI 会变,但不会。

用 useState 处理频繁更新、但 UI 不需要的数据

例如 storing mousemove 坐标:

  • 会造成大量 re-render,卡顿
  • 推荐用 useRef

在 useReducer 中修改 state(不可变规则)

错误 ❌:

jsx 复制代码
state.age = 10;
return state;

正确 ✔:

jsx 复制代码
return { ...state, age: 10 };

8. 三者组合使用示例(真实项目中常见)

示例:表单组件

状态 用哪个? 为什么
表单字段 useReducer 多字段、动作复杂
表单提交 loading useState 简单布尔值
DOM 节点(input) useRef 保存 DOM
防抖计时器 useRef 不触发渲染

9. 小结:最佳实践

场景 推荐
UI 状态简单 useState
UI 状态复杂、多字段 useReducer
数据不会用于渲染 useRef
保存 DOM 节点 useRef
保存缓存、前一次值 useRef
避免频繁 re-render useRef
需要统一管理 action useReducer

结语

useState、useRef、useReducer 都可以存储数据,但它们在 React 渲染机制中的角色完全不同。

  • useState = UI 状态
  • useRef = 自定义缓存 / DOM
  • useReducer = 逻辑复杂的状态机

掌握好这三者的边界,就能写出结构清晰且性能优秀的 React 代码。

相关推荐
GinoWi2 小时前
HTML标签 - 表格标签
前端
chushiyunen2 小时前
未设置X-XSS-Protection响应头安全漏洞
前端·xss
文心快码BaiduComate2 小时前
Comate Spec模式实测:让AI编程更精准可靠
前端·后端·前端框架
菥菥爱嘻嘻2 小时前
组件测试--React Testing Library的学习
前端·学习·react.js
豆苗学前端2 小时前
HTML + CSS 终极面试全攻略(八股文 + 场景题 + 工程落地)
前端·javascript·面试
珑墨3 小时前
【迭代器】js 迭代器与可迭代对象终极详解
前端·javascript·vue.js
Fantastic_sj3 小时前
[代码例题] var 和 let 在循环中的作用域差异,以及闭包和事件循环的影响
开发语言·前端·javascript
HashTang3 小时前
【AI 编程实战】第 3 篇:后端小白也能写 API:AI 带我 1 小时搭完 Next.js 服务
前端·后端·ai编程
三年三月3 小时前
React 中 CSS Modules 详解
前端·css