Различное поведение setState в React 17 и React 18

Я исправлял ошибку в коде FE для приложения моей компании, которое все еще использует React 17, и я заметил разное поведение при установке состояния между React 17 и React 18 (без строгого режима).

Код:

import React, { useEffect, useState } from 'react';

export default function App(props) {
  const [state, setState] = useState(0);

  console.info('render', state);

  useEffect(() => {
    (async () => {
      for (let i = 0; i < 3; i++) {
        await new Promise((res) => {
          setTimeout(res, 500);
        })

        console.info('before set state');
        setState(val => val + 1);
        console.info('after set state');
      }
    })();
  }, []);

  return (
    <h1>{state}</h1>
  );
}

Вывод консоли:

Реагировать 17

render 0
before set state
render 1
after set state
before set state
render 2
after set state
before set state
render 3
after set state

Реагировать 18

render 0
before set state
after set state
render 1
before set state
after set state
render 2
before set state
after set state
render 3

Похоже, что в React 17 setState синхронно запускает повторный рендеринг, что объясняет, почему мы видели render {num} перед выводом на консоль after set state. Однако в React 18 мы могли видеть, что повторный рендеринг происходит после консоли before set stateafter set state, а это означает, что setState не запускает повторный рендеринг сразу.

Может кто-нибудь объяснить, что здесь происходит? Это из-за параллельного режима React 18? Есть ли что-нибудь, что нам следует изменить при переходе с React 17 на React 18 из-за этого изменения поведения?

Кодыпесочница:

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
997
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

В React 18 внесены изменения в setState, чтобы можно было объединить несколько рендеров в один. В React 17 тоже была пакетная обработка, но она могла работать только для синхронного кода, который происходил в событии жизненного цикла реакции (например, useEffect) или событии dom (например, onClick). React 18 расширил эту функциональность, и теперь она работает в вашей асинхронной функции.

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

https://react.dev/blog/2022/03/29/react-v18#new-feature-automatic-batching

Есть ли что-нибудь, что нам следует изменить при переходе с React 17 на React 18 из-за этого изменения поведения?

Обычно никаких изменений не требуется; он просто повышает вашу производительность за счет объединения двух рендеров в один (ваш конкретный пример не будет объединять ни один). Если вы используете компоненты класса, есть некоторые крайние случаи, которые могут сломать ваш код. Подробности смотрите в этом посте: https://github.com/reactwg/react-18/discussions/21#discussion-3385721

Если вы хотите, чтобы рендеринг происходил синхронно, вы можете это сделать, но это требуется очень редко, и я не рекомендую это делать:

import { flushSync } from 'react-dom';
// ...
console.info('before set state');
flushSync(() => {
  setState(val => val + 1);
})
console.info('after set state');

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