- React 在响应用户交互或事件时,会将多次状态更新合并(batching)并延后到下一次渲染中一起执行。这意味着当你调用
setState
(或 useState
的 setter)后,状态并不会立即更新到最新值,而是在 React 下一轮更新阶段才生效。这样的设计可以减少不必要的重渲染,提升性能,但也带来了“看似异步”的特性。
可能造成的问题:
const [count, setCount] = useState(0)
function handleClick() {
setCount(count + 1)
console.log(count) // 第一次点击时, 这里会打印0, 第二次点击时会打印1, 因为对数据的修改是异步的, 因此这段代码读取的永远是当前值
}
const [count, setCount] = useState(0)
function handleClick() {
// 理论上想 +1,然后再 +1,变成 2
setCount(count + 1)
setCount(count + 1) // 因为读取的永远是旧值, 因此即便写了两次对源值的修改, 但是都是对之前同一值的修改, 多段代码与同一段代码的作用是一样的
console.log(count) // 第一次点击时打印0, 第二次点击时打印1
}
解决方法
- 在副作用里读取最新值 如果需要在 state 更新后执行某些逻辑,可以放到
useEffect
里监听:
const [count, setCount] = useState(0)
function handleClick() {
setCount(count + 1)
}
useEffect(() => {
console.log(count)
}, [count])
const [count, setCount] = useState(0)
function handleClick() {
setCount(count => count + 1)
setCount(count => count + 1)
}
useEffect(() => {
console.log(count) // 每次点击分别打印2,4,6,8
}, [count])
- 利用 useRef 保存最新值 当你要在事件外或不想依赖重渲染的场景下,记录最新状态:
const [count, setCount] = useState(0)
const countRef = useRef(0)
function handleClick() {
countRef.current += 1
setCount(countRef.current)
console.log(countRef.current) // 直接用 ref 拿到最新
}