这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战
翻译自:beta.reactjs.org/learn/queue…
设置 state 的值引起另一个 render 队列。但有时你可能希望执行下一次 render 队列之前对值执行多个操作。为了这样做,这篇文章有助于了解 React 如何批量更新状态。
你将会学习到:
- 什么是“批量处理(batching)”,React 如何使用它来处理多个 state 更新
- 如何在一行中对同样的 state 应用几个更新
系列文章
这是一个系列的文章,希望你能从头看,因为它们的例子之间会有先后关系的~
- React state 更新队列 - 事件处理程序中如何批量处理 state 更新
- React state 更新队列(二) - 在下一次 render 之前多次更新相同的 state
- React state 更新队列(三) - 使用更新函数(updater function)或任意值修改 state 的区别
命名约定
通常用相应 state 变量的首字母来命名 updater 函数参数:
setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);
如果您喜欢更详细的代码,另一种常见的惯例是重复完整的状态变量名,比如setEnabled(enabled => !enabled),或者使用前缀 setEnabled(prevEnabled => !prevEnabled)。
总结
- 设置 state 不会更改现有 render 中的变量,但它会在一个新的 render 中更改。
- React 在事件处理程序完成运行后处理 state 更新。这称为批处理。
- 要在一个事件中多次更新某个状态,你可以使用
setNumber(n => n + 1)更新程序函数。
实践 - 修复请求计数器
您正在开发一个艺术市场应用程序,该应用程序允许用户同时为一件艺术品提交多个订单。每次用户按下“Buy”按钮,“Pending”计数器应该增加一。三秒后,“Pending”计数器应该减少,“Completed”计数器应该增加。
但是,“Pending”计数器的行为不符合预期。当您按“Buy”时,它会减少到 -1(这应该是不可能的!)。如果您快速单击两次,两个计数器的行为似乎都无法预测。
为什么会发生这种情况?修复两个计数器。
import { useState } from 'react';
export default function RequestTracker() {
const [pending, setPending] = useState(0);
const [completed, setCompleted] = useState(0);
async function handleClick() {
setPending(pending + 1);
await delay(3000);
setPending(pending - 1);
setCompleted(completed + 1);
}
return (
<>
<h3>
Pending: {pending}
</h3>
<h3>
Completed: {completed}
</h3>
<button onClick={handleClick}>
Buy
</button>
</>
);
}
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
3
2
1
在 handleClick 事件处理程序中,pending 和 completed 的值对应于它们在单击事件时的值。对于第一次 render,pending 是 0,所以 setPending(pending - 1) 变成了 setPending(-1),这是错误的。由于你想要增加或减少计数器,而不是将它们设置为在单击期间确定的具体值,你可以改为传递更新程序函数:
import { useState } from 'react';
export default function RequestTracker() {
const [pending, setPending] = useState(0);
const [completed, setCompleted] = useState(0);
async function handleClick() {
// 这个函数
setPending(p => p + 1);
await delay(3000);
setPending(p => p - 1);
setCompleted(c => c + 1);
}
return (
<>
<h3>
Pending: {pending}
</h3>
<h3>
Completed: {completed}
</h3>
<button onClick={handleClick}>
Buy
</button>
</>
);
}
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
这确保了在增加或减少计数器时,是根据其最新的 state 而不是单击时的 state 进行操作的。