Работает ли функция обновления состояния пакета React при использовании хуков? - программирование
Подтвердить что ты не робот

Работает ли функция обновления состояния пакета 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?

4b9b3361

Ответ 1

TL; DR - если изменения состояния инициируются асинхронно (например, заключены в обещание), они не будут упакованы; если они запускаются напрямую, они будут группироваться.

Я установил песочницу, чтобы попробовать это: 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);

Я сделал две кнопки, одна из которых вызывает изменения состояния, обернутые в обещание, как в вашем примере кода, а другая - непосредственно.

Если вы посмотрите на консоль, когда вы нажмете кнопку "с обещанием", она сначала покажет a aa и bb, затем a aa и b bb.

Таким образом, ответ - нет, в этом случае он не будет визуализировать aa - bb сразу, каждое изменение состояния запускает новый рендеринг, пакетирование не выполняется.

Однако, когда вы нажмете кнопку "без обещания", консоль сразу покажет a aa и b bb.

Таким образом, в этом случае React пакетно изменяет состояние и делает один для обоих вместе.

Ответ 2

В настоящее время в React v16 и более ранних onChange по умолчанию упаковываются только обновления внутри обработчиков событий React, таких как click или onChange т.д. Так же, как обновления состояний классов упакованы аналогичным образом в хуках

Существует нестабильный 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

Если обработчиком события является react-based, он пакетирует обновления. Это верно как для вызовов setState, так и для useState.

Но он не пакетируется автоматически, если событие основано на non-react, то есть setTimeout, Promise вызывает. Короче говоря, любое событие из веб-API.