createRoot() vs render()
1. Основна різниця
| Функція | ReactDOM.render() (до React 17) | createRoot() (React 18+) |
|---|---|---|
| Тип рендерінгу | Синхронний (legacy) - React блокує потік JS, поки не закінчить рендер | Конкурентний (concurrent) - React може призупиняти, відновлювати і впорядковувати рендери за пріоритетами |
| Реакція на оновлення | Всі оновлення виконуються послідовно, без пріоритетів | Оновлення діляться на термінові / нетермінові (через startTransition, useDeferredValue та ін.) |
| Automatic batching (групування setState) | Працює тільки всередині React-подій | Працює всюди - навіть у setTimeout, промісах і async-функціях |
| Підтримка нових фіч React 18+ | Ні (не можна використовувати concurrent-можливості) | Так (включає все: concurrent rendering, Suspense SSR, streaming тощо) |
| API для SSR | ReactDOM.hydrate() | ReactDOM.hydrateRoot() |
| Керування оновленнями (flush, transitions) | Немає контролю | Є хуки useTransition, startTransition, flushSync |
2. Приклад до і після React 18
До (React 17)
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));Цей виклик використовує старий синхронний механізм. Якщо в застосунку виконується важкий рендер, React не може перервати його, і інтерфейс може "зависнути".
Після (React 18+)
import { createRoot } from "react-dom/client";
import App from "./App";
const container = document.getElementById("root");
const root = createRoot(container); // новий "корінь"
root.render(<App />);Такий спосіб створює Concurrent Root. Тепер React може:
- призупиняти рендер;
- відкладати "низькопріоритетні" оновлення;
- виконувати automatic batching;
- працювати з
startTransition()іuseDeferredValue().
3. Поведінка при оновленні стану
Приклад з асинхронним кодом:
React 17 (старий render)
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// буде 2 окремих рендери
}, 1000);React 18 (createRoot)
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// один спільний рендер завдяки Automatic Batching
}, 1000);React тепер сам об'єднує оновлення навіть поза подіями React (наприклад, у промісах або таймерах).
4. Вплив на хуки і нові можливості
createRoot() включає підтримку нових React 18 API:
useTransition()/startTransition()- контроль пріоритету оновленьuseDeferredValue()- відкладені значенняuseId()- стабільні ID між клієнтом і серверомSuspenseдля data fetching і SSR (streaming,renderToPipeableStream)
Якщо ти використовуєш ReactDOM.render(), ці фічі не працюють - React залишається в "Legacy Mode".
5. Сумісність і міграція
-
Старий
ReactDOM.render()все ще працює, але позначений як застарілий (deprecated). -
Міграція максимально проста - просто замінити один виклик:
javascript- ReactDOM.render(<App />, root); + const root = createRoot(root); + root.render(<App />); -
Решту коду (компоненти, хуки, контекст тощо) міняти не потрібно.
Підсумкове порівняння в одній фразі
ReactDOM.render()- старий синхронний рендер без пріоритетів.
createRoot()- новий конкурентний рендер, що включає можливості React 18: batching, transitions, streaming SSR і плавний UI.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.