setState у React 18
1. Головна зміна - асинхронність і "розумний" контроль пріоритетів
До React 18 усі оновлення setState() були синхронними в межах одного рендеру.
React одразу запускав перерендер, щойно ти викликав setState() (з можливим batching усередині події).
Тепер - у React 18+ (Concurrent Mode):
setState()може виконуватись асинхронно і конкурентно - React може відкласти, призупинити, об'єднати або скасувати рендер, якщо в цей момент відбуваються інші (більш пріоритетні) оновлення.
Простіше кажучи:
React тепер вирішує:
- "Це термінове оновлення? Тоді робимо одразу."
- "А це не термінове (наприклад, фільтрація списку)? Тоді відкладемо, поки користувач друкує."
2. Automatic Batching тепер працює всюди
Раніше batching (об'єднання кількох setState() в один рендер) працював тільки в подіях React (onClick, onChange тощо).
Тепер - у будь-яких контекстах: setTimeout, Promise, async/await, fetch і навіть усередині кастомних хуків.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
}, 1000);
// React 18: один спільний рендер
// React 17: два окремих рендериТобто: React тепер автоматично групує всі оновлення стану в межах однієї "ітерації" подієвого циклу.
3. setState() тепер може бути "відкладеним" (deferred)
React 18 додав API startTransition() і хук useTransition(),
які дозволяють явно позначити оновлення як "низькопріоритетні".
const [isPending, startTransition] = useTransition();
function handleChange(e) {
const value = e.target.value;
startTransition(() => {
setFilter(value); // React виконає пізніше, щоб не блокувати введення
});
}Тут:
- оновлення фільтра (низький пріоритет) виконується "у фоні";
- користувацьке введення (високий пріоритет) обробляється миттєво.
Раніше React не вмів розділяти пріоритети оновлень, тепер - вміє.
4. React може перервати або скасувати проміжний рендер
Якщо під час рендеру надходить новий стан, React може:
- скасувати вже розпочатий рендер (якщо він ще не завершений);
- почати новий із більш актуальними даними.
Це стало можливим завдяки Concurrent Rendering. У старому React 17 процес рендеру був блокуючим - React повинен був "дійти до кінця".
5. Черговість (order) і передбачуваність
Механізм "черг оновлень" (setState queue) тепер став гнучкішим:
| Сценарій | React 17 | React 18 |
|---|---|---|
Кілька setState підряд | Виконувалися синхронно в одному рендері (усередині події) | Все ще об'єднуються, але порядок гарантується навіть при асинхронних викликах |
| Оновлення в промісах | Кожен setState викликав окремий рендер | Усі об'єднуються в один спільний рендер |
Оновлення всередині startTransition | Не існувало | Оновлення виконуються пізніше, з низьким пріоритетом |
6. Приклад порівняння
React 17 (застаріла поведінка)
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
}, 1000);
// Виконає 2 рендери (кожен setState по черзі)React 18 (нова поведінка)
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
}, 1000);
// Виконає 1 рендер (automatic batching)7. Примусове оновлення (flushSync)
Якщо потрібно одразу оновити DOM, оминаючи batching,
можна використати flushSync():
import { flushSync } from 'react-dom';
flushSync(() => {
setCount(c => c + 1);
});
// React негайно перерендерить компонентЦе корисно, коли потрібно, щоб оновлення з'явилося до наступного кадру (наприклад, для точних вимірювань layout).
8. Поведінка в Strict Mode (у Dev-режимі)
У React 18 Strict Mode монтує і розмонтовує компоненти двічі,
щоб виявити побічні ефекти, пов'язані з асинхронними setState.
Якщо ти бачиш, що setState викликається двічі - це не баг, а механізм перевірки коректності побічних ефектів.
Підсумок
У React 18
setState()став:
- асинхронним і конкурентним - React може ставити оновлення в чергу і керувати їхнім пріоритетом;
- автоматично батчиться в будь-яких контекстах;
- переривним - можна скасувати проміжний рендер, якщо надійшов новий стан;
- продуктивнішим і плавнішим для UX.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.