当前位置: 首页 > 知识库问答 >
问题:

当使用钩子时,是否使用批处理状态更新函数?

关正雅
2023-03-14

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

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

共有3个答案

石思淼
2023-03-14

如果事件处理程序是基于反应的react,则它会批量更新。这对于setState或useState调用都是正确的。

但如果事件是基于非反应的,即setTimeout、Promise调用,它不会自动批处理。简而言之,任何来自Web API的事件。

吴松
2023-03-14

目前,在React v16及更早版本中,默认情况下仅批处理React事件处理程序内部的更新,如单击更改等。所以就像类一样,状态更新在钩子中也是以类似的方式批处理的

当您需要时,有一个不稳定的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

傅正阳
2023-03-14

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);

我制作了两个按钮,一个触发封装在promise中的状态更改,就像在代码示例中一样,另一个直接触发状态更改。

如果您查看控制台,当您按下“带promise”按钮时,它将首先显示a aab b,然后显示a aab bb

因此答案是否定的,在这种情况下,它不会立即渲染aa-bb,每个状态更改都会触发一个新的渲染,没有批处理。

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

因此,在本例中,React会对状态更改进行批处理,并同时对两者进行一次渲染。

 类似资料:
  • 问题内容: 对于类组件,如果在事件处理程序内部,则调用批处理。但是,如果在事件处理程序外部并使用钩子来更新状态,会发生什么? 它会立即渲染吗?还是会然后呢? 问题答案: TL; DR –如果状态更改是异步触发的(例如,包装在Promise中),则不会进行批处理;如果直接触发它们,将对其进行批处理。 我已经设置了一个沙箱来尝试一下:https : //codesandbox.io/s/402pn5l

  • 在基于类的React组件中,我们可以使用setState的函数形式定义处理函数,例如: 其中,两个参数表示传递给此组件的先前状态和更新的道具。 类似地,我们有一个函数形式,使用useState钩子在函数组件中设置状态,如下所示: 但正如我们所看到的,useState钩子中的函数形式缺少表示更新的道具的第二个参数 这是React开发团队故意留下的吗?在useState钩子中设置状态时,我们如何访问更

  • 它几乎添加了一个对象,该对象包含来自主窗体的子窗体的值。 这是我用作按钮的函数的函数。 这将一个新对象添加到一个名为的状态,该状态是一个对象数组。 提前谢了。

  • 问题内容: 我正在尝试新的React Hooks,并有一个带有计数器的Clock组件,该计数器应该每秒增加一次。但是,该值不会增加到超过一。 问题答案: 原因是传递给的闭包中的回调仅访问第一个渲染器中的变量,而无法访问后续渲染器中的新值,因为第二次未调用。 回调中的值始终为0 。 就像您熟悉的状态挂钩一样,状态挂钩有两种形式:一种是处于更新状态的状态,另一种是将当前状态传入的回调形式。您应该使用第

  • 我正在试用新的反应挂钩,并有一个时钟组件,计数器应该每秒钟增加一次。但是,该值不增加超过1。

  • 我有点像: 设置状态仍然是异步,那么等待这个调用完成的最佳方法是什么? 似乎不像那样接受回调。 在上面,我们希望按顺序运行每个setwhater调用。这是否意味着我们需要设置许多不同的useEffect挂钩来复制这种行为?