← [ BACK_TO_NOTES ]

React useState

React 中最基础的状态 Hook,用于让函数组件记住会影响渲染的值。

#frame #react #concept #evergreen

[!info] related notes

React useState

useState 是 React 最基础的状态 Hook。它让函数组件记住会影响渲染的值。

一句话定义

如果一个值会变化,而且变化后应该反映到界面上,就通常可以考虑 useState。

最小例子

const [count, setCount] = useState(0)

这里的含义是:

  • count 是当前状态值
  • setCount 是请求 React 更新状态的函数
  • 0 是初始值

它和普通变量的差别

  • 普通变量变化不会触发重渲染
  • useState 管理的值变化后,React 会重新执行组件,计算新的 UI

再进一步理解:state 更像渲染快照

在函数组件里,你拿到的 state 更像“当前这次渲染对应的一份快照”。

所以:

  • setXxx 不是立刻改掉当前变量
  • 更像是提交一次更新请求
  • 下一次渲染时,你才会读到新的 state

如果要和 Vue 3 的响应式模型正面对照,继续看:React 状态模型 vs Vue3 响应式模型

什么时候适合用它

  • 输入框内容
  • 弹窗开关
  • 当前选中项
  • 加载状态、错误状态

核心概念:不可变更新

这是 useState 最重要的习惯,必须掌握。

为什么不能直接修改

const [user, setUser] = useState({ name: 'Alice', age: 18 })

// 错误写法
user.age = 19
setUser(user)

React 更擅长根据「引用是否变化」判断状态有没有更新。你直接改了原来的 user 对象,然后又把同一个对象引用传回去,React 感受不到变化。

推荐写法:创建新对象

setUser({
  ...user,
  age: 19,
})

这里创建了一个新对象,...user 先复制原来的字段,age: 19 再覆盖掉旧值。

数组也是同理

const [list, setList] = useState([1, 2, 3])

// 错误:push 直接修改原数组
list.push(4)
setList(list)

// 推荐:创建新数组
setList([...list, 4])
// 或
setList(list.concat(4))

常见可变方法要小心

这些方法会直接修改原数组:

  • push / pop
  • shift / unshift
  • splice
  • sort / reverse

常用更新模式

// 更新对象某个字段
setForm({
  ...form,
  name: 'Tom',
})

// 数组新增项
setList([...list, newItem])

// 数组删除项
setList(list.filter(item => item.id !== id))

// 数组替换某一项
setList(list.map(item =>
  item.id === id ? { ...item, done: true } : item
))

函数式更新

为什么需要函数式更新

// 不推荐:连续更新可能有闭包问题
setCount(count + 1)
setCount(count + 1)  // 两次都基于同一个旧 count

// 推荐:基于上一个状态计算
setCount(prev => prev + 1)
setCount(prev => prev + 1)  // 两次都会加 1

函数式更新拿到的是最新状态,在连续更新、定时器回调、异步回调中更稳定。

对象也适用

setUser(prev => ({
  ...prev,
  age: prev.age + 1,
}))

惰性初始化

如果初始值计算很昂贵,用函数传入,只在初始渲染时计算一次:

const [value, setValue] = useState(() => expensiveInit());

常见误区

1. 以为 setState 后立刻能读到新值

setCount(count + 1)
console.log(count)  // 还是旧值!

setCount 更像是「告诉 React 请用这个新状态在下一次渲染时更新界面」。

2. 把可推导出的值也塞进 state

// 不推荐:fullName 可以由 firstName + lastName 推导出来
const [fullName, setFullName] = useState('')
useEffect(() => {
  setFullName(firstName + ' ' + lastName)
}, [firstName, lastName])

// 推荐:渲染时直接计算
const fullName = firstName + ' ' + lastName

3. 直接修改对象或数组再传回去

这就是上面「不可变更新」要解决的核心问题。


最短记忆方式

useState 管的是「组件的记忆」,不是普通局部变量。

状态更新与批处理

“setState / useState 更新是同步还是异步”不是最稳定的模型。状态快照、更新队列、批处理和事件中的读取边界统一看 React 事件处理与状态更新 与 useState 内部原理。

学习检查

不看正文回答:

  1. useState 与普通局部变量最核心的差别是什么?
  2. 为什么当前 render 中的 state 更像一个 snapshot?
  3. 为什么对象/数组 state 通常需要不可变更新?
  4. 连续基于前值更新时,函数式 updater 解决什么问题?
  5. 哪些值不应该进入 state,而应该直接推导?
  6. 惰性初始化解决的是首次计算成本还是每次更新成本?
  7. 给一个表单对象更新和一个计数器连续 +3 的例子,写出正确更新方式并解释原因。
创建于 2026/3/19 更新于 2026/9/30
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本