React中的批处理(附代码示例)

97 阅读2分钟

React中的批处理描述了React的内部实现细节,它将多个状态更新视为一个状态更新。这样做的好处是:多个状态更新被作为一个状态更新进行批处理,因此只触发一次组件的重新渲染,这就提高了渲染性能,特别是对于较大的React应用。让我们通过一个例子来探索React中的批处理。

import * as React from 'react';

const App = () => {
  const [counter, setCounter] = React.useState(42);
  const [clicked, setClicked] = React.useState(0);

  const handleCounter = (digit) => {
    setCounter(counter + digit);
    setClicked(clicked + 1);
  };

  console.log('component rendering');

  return (
    <div>
      <button type="button" onClick={() => handleCounter(1)}>
        Increase
      </button>
      <button type="button" onClick={() => handleCounter(-1)}>
        Decrease
      </button>

      <div>Counter: {counter}</div>
      <div>Clicked: {clicked}</div>
    </div>
  );
};

export default App;

增加减少

计数器。42

点击:0

当点击任何一个按钮时,即使在事件处理程序中发生了两次状态更新,函数组件将只重新渲染一次。自己通过检查控制台输出来验证这一行为。

在React 18之前,并非所有的状态更新都是分批进行的。例如,使用异步代码(如Promise)或第三方API(如setTimeout)的状态更新不是分批进行的,因此会触发组件的两次重新渲染(分别用于两次状态更新)。

import * as React from 'react';

const App = () => {
  const [counter, setCounter] = React.useState(42);
  const [clicked, setClicked] = React.useState(0);

  const handleCounterIncrease = () => {
    setTimeout(() => {
      setCounter(counter + 1);
      setClicked(clicked + 1);
    }, 0);
  };

  const handleCounterDecrease = async () => {
    await Promise.resolve();

    setCounter(counter - 1);
    setClicked(clicked + 1);
  };

  console.log('component rendering');

  return (
    <div>
      <button type="button" onClick={handleCounterIncrease}>
        Increase
      </button>
      <button type="button" onClick={handleCounterDecrease}>
        Decrease
      </button>

      <div>Counter: {counter}</div>
      <div>Clicked: {clicked}</div>
    </div>
  );
};

export default App;

然而,随着React在React 18中的增加,自动批处理成为了默认的。如果React开发者希望选择不使用批处理,可以使用React的flushSync顶级API。

import * as React from 'react';
import { flushSync } from 'react-dom';

const App = () => {
  const [counter, setCounter] = React.useState(42);
  const [clicked, setClicked] = React.useState(0);

  const handleCounter = (digit) => {
    flushSync(() => {
      setCounter(counter + digit);
    });
    setClicked(clicked + 1);
  };

  console.log('component rendering');

  return (
    <div>
      <button type="button" onClick={() => handleCounter(1)}>
        Increase
      </button>
      <button type="button" onClick={() => handleCounter(-1)}>
        Decrease
      </button>

      <div>Counter: {counter}</div>
      <div>Clicked: {clicked}</div>
    </div>
  );
};

flushSync() ,强迫React同步应用回调函数中的状态更新,因此强迫React立即更新DOM。其他悬而未决的状态更新也将被强制应用。毕竟,flushSync应该少用(几乎不用),除非偶尔真的需要,因为它是有注意事项的。


总之,React中的批处理只是一个实现细节,以提高状态更新的性能,从而提高每个React组件的重新渲染。