Оновлення екрана в React проходить три кроки:
- Тригер - перший рендер (
root.render) або зміна стану (setState) компонента чи його предка; - Рендер - React викликає функції компонентів, щоб дізнатися, що має бути на екрані. Для оновлень порівнює новий результат з попереднім (reconciliation) і визначає мінімальні зміни;
- Коміт - React застосовує зміни до DOM, потім браузер відмальовує екран і виконуються ефекти.
Важливі наслідки:
- рендер - чисте обчислення. Його можна перервати, відкласти, виконати повторно або двічі (StrictMode). Тому в тілі компонента не можна мутувати зовнішні змінні, робити запити чи змінювати DOM - лише обчислювати JSX з props і стану;
- рендер ≠ зміна DOM. Якщо результат рендеру той самий, DOM не чіпається. Зайві рендери коштують процесорного часу на обчислення, але не на роботу з DOM;
- у конкурентному режимі рендер нетермінового оновлення (transition) можна перервати заради термінового (введення в поле), а коміт завжди синхронний і неподільний - користувач не бачить «половини» оновлення.
Групування оновлень (batching). Кілька setState в одному обробнику дають один рендер:
function handleClick() {
setCount((c) => c + 1);
setFlag((f) => !f);
setItems([]);
// один рендер після завершення обробника
}
З React 18 групування автоматичне скрізь: у таймерах, промісах, нативних обробниках подій. У React 17 і раніше воно працювало лише в обробниках подій React - setState у setTimeout давав окремий рендер на кожен виклик.
Черга оновлень. React обробляє оновлення по черзі: значення - замінює, функцію - викликає з результатом попереднього кроку:
setNumber(number + 5); // замінити на 5
setNumber((n) => n + 1); // 5 + 1
setNumber(42); // замінити на 42
// результат 42
Коли потрібно оновити DOM негайно (виміряти щойно доданий елемент) - flushSync з react-dom. Це рідкісний виняток: він ламає групування й шкодить продуктивності.
Коли компонент перерендерюється: змінився його стан, перерендерився батько (навіть якщо props ті самі - якщо компонент не обгорнуто в memo і його не оптимізує React Compiler) або змінився контекст, який він читає.