React state 更新队列(三) - 使用更新函数(updater function)或任意值修改 state 的区别

595 阅读3分钟

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

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

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

你将会学习到:

  • 什么是“批量处理(batching)”,React 如何使用它来处理多个 state 更新
  • 如何在一行中对同样的 state 应用几个更新

系列文章

这是一个系列的文章,希望你能从头看,因为它们的例子之间会有先后关系的~

如果在替换 state 后更新 state 会发生什么

前面两篇文章我们讲了,在一个事件处理程序里,连续使用 setNumber(n+1) 的话,React 会想处理你点菜的服务员一样,只会更新一次(只点了一个菜)。但是如果更新多次的话,也是可以实现的,可以用 setNumber(n => n + 1) 这种形式,每次执行时参数 n,都是上一次更新计算后的最新值。

那么如果我们混用这两种写法,会发生什么呢?比如下面的例子

<button onClick={() => {
  setNumber(number + 5);
  setNumber(n => n + 1);
}}>
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setNumber(n => n + 1);
      }}>Increase the number</button>
    </>
  )
}

发现点完按钮后 number +6 了,这是为什么呢?

下面是事件处理程序告诉 React 做的事情:

  1. setNumber(number + 5)number 是 0, 于是 setNumber(0 + 5). React 把 “替换为 5  增加到它的队列里。
  2. setNumber(n => n + 1): n => n + 1 是一个更新函数。 React 将这个函数添加到其队列中。

在下一次渲染期间,React 会遍历 state 队列:

更新队列n返回值。
“替换为 50 (未使用)5
n => n + 155 + 1 = 6

React 将 6 存储为最终结果并从 useState 返回它。

您可能已经注意到 setState(x) 实际上与 setState(n => x) 类似,但 n 未使用到!

如果在更新 state 后替换 state 会发生什么

和上面的例子相反,这次我们先更新 state

<button onClick={() => {
  setNumber(number + 5);
  setNumber(n => n + 1);
  setNumber(42);
}}>
import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        setNumber(n => n + 1);
        setNumber(42);
      }}>Increase the number</button>
    </>
  )
}

下面是 React 在执行这个事件处理程序时如何通过这些代码行工作:

  1. setNumber(number + 5)number 值是 0, 于是 setNumber(0 + 5). React 把  “替换为 5  放入它的队列中。
  2. setNumber(n => n + 1): n => n + 1 是一个更新函数。 React 将这个函数添加到其队列中。
  3. setNumber(42): React 把  “替换为 42  放入它的队列中。

在下一次渲染中,React 通过 state 队列:

更新队列n返回值
“替换为 50 (未使用)5
n => n + 155 + 1 = 6
“替换为 426 (未使用)42

然后 React 存储 42 作为最终结果并从 useState 返回。

总结

总而言之,您可以通过以下方式思考传递给 setNumber state setter 的内容:

  • 更新函数(例如 n => n + 1),会被添加到队列中。
  • 任何其他值(例如数字 5)都会将“替换为 5”添加到队列中,并会忽略已经排队的内容。 事件处理程序完成后,React 将触发重新渲染。在重新渲染期间,React 将处理队列。更新程序功能在渲染期间运行,所以更新函数必须是纯函数(pure function)并且只返回结果。不要尝试从它们内部设置状态或运行其他副作用。严格模式下,React 会运行每个更新器函数两次(但丢弃第二个结果)以帮助您发现错误。