762 字
4 分钟
useState

先看一个不工作的写法#

function Counter() {
let count = 0
return <button onClick={() => count++}>点了 {count} 次</button>
}

点多少下都显示「点了 0 次」。count 是渲染时的局部变量,函数每渲染一次就重建一遍,改它不会让 React 知道该重渲染。

useState 补上的就是这块记忆#

import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>点了 {count} 次</button>
}

useState(0) 返回一个两元数组:当前值,与更新函数。命名习惯是 [x, setX]。

关键在 setCount 到底做了什么。它不改 count,而是请 React 用新值再渲染一次。本次渲染里的 count 是一个不会再变的快照,所以下面这段打印的是旧值:

const handleClick = () => {
setCount(count + 1)
console.log(count) // 旧值
}

官方把这条讲成「状态是一次渲染的快照」(State as a Snapshot)。要拿最新值,用更新函数形式,或者干脆在渲染时算。

依赖旧值时用更新函数#

setCount(count + 1) // 用的是本次渲染的快照
setCount(prev => prev + 1) // 用的是最新的待处理值

连续调用两次时差别就出来了。同一个事件里写两次 setCount(count + 1),两次读到的 count 是同一个快照,结果只加 1;写两次 setCount(prev => prev + 1) 则加 2。官方在 Queueing a Series of State Updates 里把这件事讲成排队:更新函数依次作用在前一个结果上。

对象与数组要返回新引用#

useState 与类组件的 this.setState 不同,它不做属性合并,也不检查你有没有改原对象。直接改字段再 set 回去,React 拿到的是同一个引用,界面不动:

// 不会重渲染
function handleClick() {
user.name = '新名字'
setUser(user)
}
// 返回新对象
function handleClick() {
setUser(prev => ({ ...prev, name: '新名字' }))
}

数组同理,用 [...list, item]、list.filter(...)、list.map(...) 这类返回新数组的写法,不要 push。完整清单在官方 Updating Objects in State 与 Updating Arrays in State。

初始值只在首次渲染用到#

useState(initial) 里的初始值只在组件第一次挂载时参与,之后重渲染会忽略它。

如果初始值要算出来(读 localStorage、遍历大数组),传函数做惰性初始化:

const [rows, setRows] = useState(() => buildRows(props.data))

直接写 useState(buildRows(props.data)) 的话,每次渲染都会算一遍再丢掉。

只能在顶层调用#

Hooks 的调用顺序就是 React 对号入座的依据,所以 useState 只能写在组件或自定义 Hook 的顶层:

// 不行
function Bad({ show }) {
if (show) {
const [count, setCount] = useState(0)
}
return null
}

条件分支要挪到 useState 之后,改为控制渲染内容。规则全文见 Rules of Hooks。

什么数据不该放状态#

能由 props 或已有状态算出来的,就别再存一份。冗余状态最难保持一致:

// 冗余:fullName 完全可以算出来
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [fullName, setFullName] = useState('')

渲染时直接拼 firstName 与 lastName 就行。这一类的判断方法见官方 Choosing the State Structure。

第 11 篇,共 39 篇本文属于系列

useState
https://www.choria.top/posts/hook-use-state/
作者
乔瑞雅·克林威尔
发布于
2026-05-02
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取

最后更新于 ,距今已过 190 天

部分内容可能已过时