Я исправлял ошибку в коде 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 из-за этого изменения поведения?
Кодыпесочница:



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


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