React state 更新队列(四) - 总结和实践

926 阅读3分钟

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

翻译自:beta.reactjs.org/learn/queue…

设置 state 的值引起另一个 render 队列。但有时你可能希望执行下一次 render 队列之前对值执行多个操作。为了这样做,这篇文章有助于了解 React 如何批量更新状态。

你将会学习到:

  • 什么是“批量处理(batching)”,React 如何使用它来处理多个 state 更新
  • 如何在一行中对同样的 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 事件处理程序中,pendingcompleted 的值对应于它们在单击事件时的值。对于第一次 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 进行操作的。