state
一个state变量的值永远不会在一次渲染的内部发生变化
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
alert(number);
}}>+5</button>
</>
)
}
alert的值是0,在渲染的onClick内部,number的值即使在调用setNumber(number + 5)之后也还是0,它的值在React调用组件时就被固定了。但如果在这个提示框上加上一个定时器, 使得它在组件重新渲染 之后 才触发,alert就会显示5。
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);
setTimeout(() => {
alert(number);
}, 3000);
}}>+5</button>
</>
)
}
React会等到事件处理函数中的所有代码都运行完毕再处理你的state更新,这可能会让你想起餐厅里帮你点菜的服务员。服务员不会在你说第一道菜的时候就跑到厨房!相反,他们会让你把菜点完,让你修改菜品,甚至会帮桌上的其他人点菜。这让你可以更新多个 state 变量——甚至来自多个组件的 state 变量——而不会触发太多的重新渲染。但这也意味着只有在你的事件处理函数及其中任何代码执行完成 之后,UI 才会更新。这种特性也就是 批处理。
在下次渲染前多次更新同一个state,你可以像setNumber(n => n + 1)这样传入一个根据队列中的前一个state计算下一个state的函数,而不是像setNumber(number+1)这样传入下一个state值。
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>+3</button>
</>
)
}
n => n + 1被称为更新函数,当你将它传递给一个state设置函数时:
- React 会将此函数加入队列,以便在事件处理函数中的所有其他代码运行后进行处理。
- 在下一次渲染期间,React 会遍历队列并给你更新之后的最终 state。
下面是 React 在执行事件处理函数时处理这几行代码的过程:
setNumber(n => n + 1):n => n + 1是一个函数。React 将它加入队列。setNumber(n => n + 1):n => n + 1是一个函数。React 将它加入队列。setNumber(n => n + 1):n => n + 1是一个函数。React 将它加入队列。
当你在下次渲染期间调用 useState 时,React 会遍历队列。之前的 number state 的值是 0,所以这就是 React 作为参数 n传递给第一个更新函数的值。然后 React 会获取你上一个更新函数的返回值,并将其作为 n传递给下一个更新函数,以此类推:
| 更新队列 | n | 返回值 |
|---|---|---|
n => n + 1 |
0 |
0 + 1 = 1 |
n => n + 1 |
1 |
1 + 1 = 2 |
n => n + 1 |
2 |
2 + 1 = 3 |
React 会保存 3为最终结果并从 useState中返回。
这就是为什么在上面的示例中点击“+3”正确地将值增加“+3”。
事件处理函数执行完成后,React 将触发重新渲染。在重新渲染期间,React 将处理队列。更新函数会在渲染期间执行,因此 更新函数必须是纯函数 并且只 返回 结果。不要尝试从它们内部设置 state 或者执行其他副作用。在严格模式下,React 会执行每个更新函数两次(但是丢弃第二个结果)以便帮助你发现错误。

浙公网安备 33010602011771号