Зачем библиотека React делится на пакеты ReactDOM и React?
Основы React
Разделение на react и react-dom — это архитектурная граница между «что рендерить» (платформонезависимое ядро) и «куда рендерить» (конкретная платформа).
Мотивация: один React — много платформ
Ядро (пакет react) содержит всё, что не зависит от среды: компоненты, элементы, хуки, контекст (сам reconciler — пакет react-reconciler, который встраивается в каждый рендерер). Рендереры привязывают его к платформам:
- react-dom — браузерный DOM (плюс react-dom/server для SSR);
- react-native — нативные iOS/Android-компоненты;
- кастомные рендереры (react-reconciler): react-three-fiber (3D), ink (терминал), react-pdf...
Логика компонентов и хуков одна и та же везде; меняется только слой отрисовки. Не будь разделения, React Native пришлось бы тащить весь DOM-код, а web — нативный.
Что это даёт на практике
- Кроссплатформенный код: компоненты без DOM-специфики зависят только от react — их можно шарить между web и native.
- Изолированная эволюция: ядро и рендереры версионируются и развиваются согласованно, но код чётко разделён; SSR-API живут в react-dom/server и не попадают в клиентский бандл без нужды.
- Чистота зависимостей: библиотеки хуков/логики зависят только от react и работают с любым рендерером.
Как выглядит граница в коде
import { useState } from 'react'; // ядро: логика
import { createRoot } from 'react-dom/client'; // рендерер: браузер
createRoot(document.getElementById('root')).render(<App />);Хуки, memo, Context — из react; порталы, createRoot, hydrateRoot, flushSync — из react-dom.
Коротко для собеседования
Деление отделяет платформонезависимое ядро (компоненты, хуки, контекст; дифф выполняет reconciler внутри рендерера) от рендерера конкретной среды (react-dom — браузер). Это позволяет тому же ядру работать в React Native, three-fiber и др., шарить код между платформами и держать SSR/DOM-API отдельно от логики.