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

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
86
0
31 673
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

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.info('a', a);
  console.info('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 и b b, затем a aa и b bb.

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

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

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

Кстати, попробовал без Promise.resolve. setA и setB были объединены в пакет, как и ожидалось, аналогично компоненту класса (setState вызывается в обработчике событий).

vadirn 29.10.2018 17:10

Примечание от github.com/facebook/react/issues/10231#issuecomment-31664495‌ 0 - This is implementation detail and may change in future versions.

Aprillion 14.07.2019 17:52

Я думаю, что проблема, на которую ссылается @Aprillion, не относится к хукам, это касается компонентов класса

ned 07.10.2019 15:20

@ned Хотя проблема была создана до хуков, сам комментарий применим к любой реализации состояния, приложения не должны полагаться на текущие детали оптимизации.

Aprillion 07.10.2019 16:22

Но будут ли они выполнять по порядку?

Jo Momma 06.05.2020 02:10

буквы, используемые в этом примере, ужасно сбивают с толку. также правильно ли b b? Думаю, это опечатка.

temporary_user_name 26.09.2020 22:54

@ Contemporary_user_name это не опечатка, когда вы обрабатываете щелчок с обещанием, изменения состояния не являются пакетными, поэтому при первом рендеринге значение b все еще равно «b», следовательно, оператор журнала «b b»

Patrick Hund 27.09.2020 15:33

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

Существует нестабильный API для принудительной пакетной обработки вне обработчиков событий в редких случаях, когда это необходимо.

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

Планируется, что в будущей версии все обновления состояния будут загружены при ответе, вероятно, версии 17 или выше.

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

Если без кода синхронизации обновления состояния выполняются в пакетном режиме, а обновления асинхронного кода - нет.

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.info("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

Примечание от github.com/facebook/react/issues/10231#issuecomment-31664495‌ 0 - This is implementation detail and may change in future versions.

Aprillion 14.07.2019 17:51

изменения состояния внутри componentDidMount также группируются.

holmberd 18.10.2019 16:27

Насколько я могу судить, React 17.0.1 по-прежнему не выполняет пакетные обновления вне обработчиков событий React.

aldel 24.03.2021 20:50

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

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

ответ уже дан @Patrick Hund .. Просто хочу здесь обновить, что с помощью React 18 пакетное обновление состояний возможно для Promise, также по умолчанию setTimeout.

Until React 18, we only batched updates during the React event handlers. Updates inside of promises, setTimeout, native event handlers, or any other event were not batched in React by default.

Ознакомьтесь с подробным объяснением. https://github.com/reactwg/react-18/discussions/21

Другие вопросы по теме