使用钩子时 React 批处理状态更新功能吗?

新手上路,请多包涵

对于类组件, this.setState 如果在事件处理程序内部调用批处理。但是,如果在事件处理程序之外更新状态并使用 useState 挂钩会发生什么?

 function Component() {
  const [a, setA] = useState('a');
  const [b, setB] = useState('b');

  function handleClick() {
    Promise.resolve().then(() => {
      setA('aa');
      setB('bb');
    });
  }

  return <button onClick={handleClick}>{a}-{b}</button>
}

它会立即呈现 aa - bb 吗?或者是 aa - b 然后是 aa - bb

原文由 vadirn 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 769
2 个回答

TL;DR – 如果状态更改是异步触发的(例如包装在一个 Promise 中),它们将不会被批处理;如果它们被直接触发,它们将被批处理。

我已经设置了一个沙箱来试试这个: https ://codesandbox.io/s/402pn5l989

 import React, { Fragment, useState } from 'react';
import ReactDOM from 'react-dom';

import './styles.css';

function Component() {
  const [a, setA] = useState('a');
  const [b, setB] = useState('b');
  console.log('a', a);
  console.log('b', b);

  function handleClickWithPromise() {
    Promise.resolve().then(() => {
      setA('aa');
      setB('bb');
    });
  }

  function handleClickWithoutPromise() {
    setA('aa');
    setB('bb');
  }

  return (
    <Fragment>
    <button onClick={handleClickWithPromise}>
      {a}-{b} with promise
    </button>
    <button onClick={handleClickWithoutPromise}>
      {a}-{b} without promise
    </button>
      </Fragment>
  );
}

function App() {
  return <Component />;
}

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

我制作了两个按钮,一个触发包装在您的代码示例中的承诺中的状态更改,另一个直接触发状态更改。

If you look at the console, when you hit the button “with promise”, it will first show a aa and b b , then a aa and b bb .

所以答案是否定的,在这种情况下,它不会立即渲染 aa - bb ,每次状态变化都会触发新的渲染,没有批处理。

但是,当您单击“无承诺”按钮时,控制台将立即显示 a aab bb

因此,在这种情况下,React 会批量处理状态更改并同时为两者进行一次渲染。

原文由 Patrick Hund 发布,翻译遵循 CC BY-SA 4.0 许可协议

目前在 React v16 和更早版本中,只有 React 事件处理程序内部的更新,例如 clickonChange 等默认情况下是批处理的。所以就像在钩子中以类似的方式对类状态更新进行批处理一样

在极少数情况下,有一个不稳定的 API 可以在您需要时强制在事件处理程序之外进行批处理。

 ReactDOM.unstable_batchedUpdates(() => { ... })

有计划在未来版本中对所有状态更新进行批处理,可能是 v17 或更高版本。

现在,如果事件处理程序中的状态更新调用在异步函数中或由于异步代码而触发,则它们不会被批处理,而直接更新将被批处理

在没有同步代码的情况下,状态更新是批处理的,而异步代码更新不是

function App() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  // async update from useEffect
  useEffect(() => {
    setTimeout(() => {
      setCount1(count => count + 1);
      setCount2(count => count + 2);
    }, 3000);
  }, []);

  const handleAsyncUpdate = async () => {
    await Promise.resolve("state updated");
    setCount1(count => count + 2);
    setCount2(count => count + 1);
  };

  const handleSyncUpdate = () => {
    setCount1(count => count + 2);
    setCount2(count => count + 1);
  };

  console.log("render", count1, count2);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button type="button" onClick={handleAsyncUpdate}>
        Click for async update
      </button>
      <button type="button" onClick={handleSyncUpdate}>
        Click for sync update
      </button>
    </div>
  );
}

https://codesandbox.io/s/739rqyyqmq

原文由 Shubham Khatri 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进