web-dev-qa-db-ja.com

フックを使用するときに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

23
vadirn

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

2つのボタンを作成しました。1つはコード例のようにプロミスにラップされた状態変更をトリガーし、もう1つは状態変更を直接トリガーします。

コンソールを見ると、「約束」ボタンを押すと、最初にa aaおよびb b、次にa aaおよびb bb

したがって、答えは「いいえ」です。この場合、aa - bbすぐに、状態の変化ごとに新しいレンダリングがトリガーされ、バッチ処理は行われません。

ただし、「約束なし」ボタンをクリックすると、コンソールにa aaおよびb bb 直ちに。

したがって、この場合、Reactは状態の変更をバッチ処理し、両方に対して1つのレンダリングを行います。

25
Patrick Hund

現在React v16以前では、clickonChangeなどの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

3
Shubham Khatri