快照的概念
在 React 中,state 是组件状态的表现形式,它是一个组件内部可变的状态数据。React 的官方文档中提到 state 是一个快照(snapshot),这个概念主要是指 React 的状态更新机制。
当你调用 setState 方法来更新组件的状态时,React 会异步地将这些更新排队。这意味着,当你在调用 setState 后立即读取 state,你可能会得到旧的 state 值,而不是你刚刚设置的新值。这是因为 React 可能还没有应用这个更新。
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 1);
setNumber(number + 1);
setNumber(number + 1);
}}>+3</button>
</>
)
}
上面的例子,最终输出的结果是什么呢?
React 函数组件 Counter
中,有一个按钮用于增加状态 number
的值。当按钮被点击时,会触发 onClick
事件处理函数,在该函数中连续三次调用 setNumber(number + 1)
。
由于 React 的状态更新(通过 setNumber
)是异步的,这些更新会被放入一个队列中,然后 React 将它们批量处理以提高性能。这意味着,尽管你在事件处理函数中连续三次调用了 setNumber
,实际的状态更新可能不会立即发生,而是会被合并和延迟处理。
具体到这个例子中,尽管你调用了三次 setNumber
,React 会将这些更新合并,最终只增加一次状态值,因为最后一次状态更新覆盖了前两次的更新。所以,即使按钮上写的是 “+3”,实际上 number
状态只增加了 1。
至于何时执行更新,React 通常会在当前的事件循环结束后异步执行状态更新。这意味着,如果用户界面在执行 setNumber
调用时是响应的,那么状态更新和组件重新渲染会在浏览器的下一次绘制周期中进行,通常是在当前执行栈清空并且宏任务(如 setTimeout
或 requestAnimationFrame
)队列为空时。这样可以确保在更新发生前,用户的操作已经被处理完毕,从而提高应用的响应性和性能。
如果你希望每次点击都能使 number
增加 3,你需要这样写 setNumber
的调用:
<button onClick={() => {
setNumber(n => n+1)
setNumber(n => n+1)
setNumber(n => n+1)
}}>+3</button>
总结
- 设置 state 不会更改现有渲染中的变量,但会请求一次新的渲染。
- React 会在事件处理函数执行完成之后处理 state
- 更新。这被称为批处理。 要在一个事件中多次更新某些 state,你可以使用 setNumber(n => n + 1) 更新函数。