无废话之 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 代码。

相关推荐
UXbot1 分钟前
AI 原型工具零设计基础操作指南与功能解析(2026)
前端·ui·产品经理·原型模式·web app
yuzhiboyouye37 分钟前
VO一般java后端怎么转换成前端想要的数据
java·前端·状态模式
小脑斧1231 小时前
从范式重构到工程落地:OpenTiny NEXT 引领前端智能化新范式
前端·hermesagent·opentiny next
小江的记录本1 小时前
【AI大模型选型指南】《2026年5月(最新版)国内外主流AI大模型选型指南》(企业版)
前端·人工智能·后端·ai作画·aigc·ai编程·ai写作
幽络源小助理1 小时前
最新轻量美化表白墙系统源码v2.0_带后台版_附搭建教程
前端·开源·源码·php源码
qq_381338501 小时前
前端状态管理新范式:Zustand、Jotai 与 Preact Signals 深度对比
前端·arcgis
布局呆星1 小时前
Vue Router 笔记(二):正则路由、组件通信与动态路由
前端·javascript·vue.js
丑八怪大丑1 小时前
HTML&CSS
前端·css·html
团象科技1 小时前
全渠道出海布局之下,多币种云结算承担着怎样的作用
前端·人工智能
lolo大魔王2 小时前
Go 语言 Web 框架 Gin 入门详解
前端·golang·gin