Як виправити помилку «Target container is not a DOM element» у React 18

Найважливіше виправлення таке: переконайтеся, що значення, яке ви передаєте у createRoot(), є фактичним елементом DOM, який уже існує. У React 18 стандартна точка входу для клієнта виглядає так:

import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');

if (!container) {
  throw new Error('Root element not found');
}

const root = createRoot(container);
root.render(<App />);

Якщо document.getElementById('root') повертає null, або якщо ви випадково передаєте React-елемент, такий як <App />, у createRoot(), React не може створити корінь і може повідомити про помилку «Target container is not a DOM element». У власній документації з усунення несправностей createRoot React визначає цю помилку саме такими словами: значення, передане у createRoot, не є вузлом DOM.

Цей посібник починається з цієї найбільш ймовірної причини, а потім розглядає проблеми з часом виконання, помилки міграції на React 18, серверний рендеринг, портали, TypeScript та середовища тестування, щоб ви могли зупинитися, щойно знайдете випадок, що відповідає вашому додатку.

Крок 1: Доведіть, що саме ви передаєте у createRoot()

Перш ніж змінювати конфігурацію, виведіть контейнер у консоль:

const container = document.getElementById('root');
console.log(container);

const root = createRoot(container);

Якщо консоль виводить null, то React не є частиною, яка першою зазнала збою. Браузер не знайшов елемента з таким ID у момент виконання вашого коду. У офіційному розділі усунення несправностей React зазначено невідповідність ID та виконання до того, як вузол DOM існує, як поширені причини.

Якщо консоль виводить щось на кшталт <div id="root"></div>, то контейнер існує, і вам слід перейти до перевірок API React, SSR, порталів або середовища нижче.

Застосовується, коли: помилка з’являється одразу під час запуску додатка, особливо у файлах main.jsx, index.jsx або index.tsx.

Дія: не вгадуйте. Виведіть точне значення, передане у createRoot(). Якщо це null, виправте причину збою пошуку в DOM, перш ніж чіпати код компонентів.

Ілюстрація редактора коду, згенерована ШІ, що показує помилку React «Target container is not a DOM element»
Ілюстрація, згенерована ШІ, що демонструє помилку контейнера React 18 у консолі розробника. Це не скріншот із реального додатка або React DevTools.

Крок 2: Зіставте ID в HTML із пошуком у JavaScript

Найпростіша реальна причина — невідповідність між вашим HTML і JavaScript.

Ваш HTML може містити:

<div id="app"></div>

тоді як ваш вхідний файл React шукає:

document.getElementById('root')

Ці назви мають збігатися. Або змініть розмітку:

<div id="root"></div>

або змініть пошук:

const container = document.getElementById('app');

У поточній довідці React щодо createRoot використовується стандартний приклад document.getElementById('root'), але root не є магічним обов’язковим ID. Будь-який реальний елемент DOM можна використовувати як кореневий контейнер. Важливою умовою є те, що елемент існує і що пошук повертає його.

Застосовується, коли: ви нещодавно змінили шаблон HTML, мігрували з Create React App на Vite або інший бандлер, вбудували React у наявну сторінку з серверним рендерингом або перейменували елемент монтування.

Дія: шукайте у проєкті як id="root", так і getElementById('root'). Якщо проєкт навмисно використовує інший ID, узгодьте HTML і JavaScript.

Ілюстрація редактора коду, згенерована ШІ, що підсвічує div з id root у файлі HTML
Ілюстрація, згенерована ШІ, що демонструє відповідний елемент монтування id="root". Це концептуальний вигляд редактора коду, а не скріншот конкретного шаблону фреймворку.

Крок 3: Використовуйте API кореня React 18 у правильному порядку

React 18 представив клієнтський API createRoot. Офіційний посібник з оновлення до React 18 демонструє міграцію зі старого патерну ReactDOM.render до:

import { createRoot } from 'react-dom/client';

const container = document.getElementById('root');
const root = createRoot(container);
root.render(<App />);

Дивно легкою помилкою є переставляння ролей DOM-контейнера та React-компонента:

// Неправильно
createRoot(<App />);

React явно зазначає це як ще одну поширену причину помилки «Target container is not a DOM element». createRoot() отримує вузол DOM; root.render() отримує вузол React.

Ще одна помилка міграції — ментальне перенесення сигнатури React 17 і спроба передати контейнер у root.render():

// Неправильна ментальна модель
root.render(<App />, container);

// Правильно
const root = createRoot(container);
root.render(<App />);

У довідці React щодо createRoot зазначено, що root.render(reactNode) приймає вузол React, тоді як контейнер належить до createRoot(domNode).

TypeScript: не плутайте non-null assertion із виправленням під час виконання

У посібнику з оновлення до React 18 показано createRoot(container!) як форму для TypeScript. Знак оклику є твердженням під час компіляції: він повідомляє TypeScript, що ви вважаєте значення не null. Він не створює відсутній HTML-елемент під час виконання.

Безпечніший шаблон під час налагодження:

const container = document.getElementById('root');

if (container === null) {
  throw new Error('Expected #root to exist');
}

createRoot(container).render(<App />);

Це дає більш корисну помилку, специфічну для додатка, якщо HTML і JavaScript розійдуться.

Застосовується, коли: проблема з’явилася під час міграції з React 17 на React 18, після копіювання вхідного файлу з іншого проєкту або лише у збірках TypeScript, де було додано !, щоб приглушити попередження компілятора.

Дія: перевірте послідовність викликів: пошук у DOM → createRoot(container) → root.render(<App />). Під час налагодження віддавайте перевагу явній перевірці на null, а не сліпому твердженню container!.

Ілюстрація редактора коду, згенерована ШІ, що показує перевірку на null перед createRoot та root render у React 18
Ілюстрація, згенерована ШІ, що демонструє захисний шаблон запуску React 18. Код показано як концептуальний приклад, а не як вивід із реального проєкту.

Крок 4: Переконайтеся, що ваш код запуску виконується після того, як цільовий елемент існує

ID може бути ідеально написаним і все одно повертати null, якщо ваш скрипт виконується до того, як браузер розпарсив елемент. У документації з усунення несправностей React спеціально попереджають, що скрипт бандла не може бачити вузли DOM, які з’являються пізніше в HTML, якщо виконання відбувається занадто рано.

Це стосується переважно власних HTML-сторінок і старіших налаштувань вбудовування. Поширений безпечний макет — розмістити елемент монтування перед скриптом:

<body>
  <div id="root"></div>
  <script type="module" src="/src/main.jsx"></script>
</body>

Якщо ви контролюєте власний скрипт, який може виконуватися до завершення парсингу, іншим захисним варіантом є очікування DOMContentLoaded:

function start() {
  const container = document.getElementById('root');
  if (!container) throw new Error('Root element not found');
  createRoot(container).render(<App />);
}

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', start);
} else {
  start();
}

Не додавайте цей обгортку автоматично до кожного проєкту React. Сучасні бандлери та фреймворки зазвичай самі керують розміщенням вхідного скрипту та семантикою завантаження. Якщо стандартний додаток Vite, Next.js, Remix або фреймворку раптово починає видавати цю помилку, спочатку шукайте змінений шаблон, ID монтування, власну інтеграцію або код, що виконується поза очікуваною браузерною точкою входу.

Застосовується, коли: той самий ID існує у фінальному HTML, але пошук все ще повертає null під час запуску, особливо у вручну зібраній HTML-сторінці, шаблоні CMS, вбудованому віджеті або інтеграції зі стороннім скриптом.

Дія: перевірте фактичний вихідний код сторінки та порядок виконання. Переконайтеся, що вузол монтування існує до коду, який викликає createRoot().

Ілюстрація редактора коду, згенерована ШІ, що показує захист DOMContentLoaded перед створенням кореня React
Ілюстрація, згенерована ШІ, що демонструє захист готовності DOM для власного завантаження React. Це не обов’язковий шаблон для кожного додатка React 18; використовуйте його лише тоді, коли час запуску дійсно є проблемою.

Якщо ваша сторінка має серверний рендеринг, використовуйте hydrateRoot замість цього

Існує важлива умова, коли валідний елемент DOM недостатньо, щоб зробити createRoot() правильним API. Якщо контейнер уже містить HTML, згенерований React на сервері або під час збірки, документація React каже використовувати hydrateRoot() замість createRoot().

import { hydrateRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');

if (!container) {
  throw new Error('Root element not found');
}

hydrateRoot(container, <App />);

Причина відрізняється від помилки цільового контейнера. createRoot() керує коренем із клієнтським рендерингом і при першому рендерингу очищає наявний HTML всередині цього кореня. hydrateRoot() прикріплює React до HTML, який уже був створений React на сервері. В офіційній документації createRoot це явно зазначено як пастку серверного рендерингу.

Застосовується, коли: у вас є HTML React із серверним рендерингом, статична генерація, що видає розмітку React, або фреймворк, який гідратує HTML у браузері.

Дія: не «виправляйте» SSR, замінюючи серверну розмітку порожнім контейнером. Використовуйте точку входу гідратації фреймворку або hydrateRoot() React відповідно.

Якщо це ціль для модального вікна або підказки, вам може знадобитися createPortal, а не інший корінь

Іноді розробники бачать відсутній контейнер, намагаючись відобразити модальне вікно, підказку, область сповіщень або оверлей поза головним деревом додатка. У документації React зазначено, що коли ви хочете, щоб JSX з’являвся в іншому місці DOM, використовуйте createPortal(), а не створюйте ще один корінь лише для цього дочірнього UI.

import { createPortal } from 'react-dom';

function Modal({ children }) {
  const modalRoot = document.getElementById('modal-root');

  if (!modalRoot) return null;

  return createPortal(children, modalRoot);
}

В офіційній документації createPortal зазначено, що ціль портали повинна вже існувати. Тому портали можуть створювати пов’язаний клас проблем із контейнером, якщо modal-root відсутній, але архітектурне виправлення не обов’язково полягає в тому, щоб «викликати createRoot ще раз».

Застосовується, коли: контейнер, що не працює, не є головним коренем вашого додатка, а є місцем призначення для оверлея або вузлом, яким керують поза нормальною позицією компонента в DOM.

Дія: тримайте один нормальний корінь додатка, якщо вам дійсно не потрібні кілька незалежних коренів. Для UI у стилі модальних вікон у межах того самого додатка React віддавайте перевагу порталі до наявного вузла DOM.

Що робити, якщо помилка виникає лише в тестах?

Тест може не пройти з тієї ж фундаментальної причини: очікуваний контейнер ніколи не був вставлений у тестовий DOM. Якщо ваш тест вручну викликає createRoot(document.getElementById('root')), переконайтеся, що налаштування тесту дійсно створює цей вузол перед рендерингом.

beforeEach(() => {
  document.body.innerHTML = '<div id="root"></div>';
});

Однак багато бібліотек тестування React керують контейнерами за вас. Якщо ви вже використовуєте допоміжну функцію render() фреймворку тестування, ручне створення кореня React може бути непотрібним і може зробити налаштування тесту більш крихким.

Застосовується, коли: розробка працює в браузері, але Jest, Vitest, JSDOM або інше тестове середовище видає помилку контейнера.

Дія: перевірте налаштування DOM тесту, а не продакшн-файл index.html. Підтвердіть, що вузол існує в середовищі, де фактично виконується код, що не працює.

Що робити, якщо document недоступний?

Якщо код, який викликає document.getElementById(), виконується на сервері або в іншому небраузерному середовищі, у вас інша проблема інтеграції. Клієнтські API React у react-dom/client призначені для рендерингу у вузли DOM браузера. Серверний рендеринг використовує API з react-dom/server, і фреймворки зазвичай розділяють серверні та клієнтські точки входу.

Застосовується, коли: помилка з’являється під час серверного рендерингу, кроку збірки Node або коду, спільного для серверних і браузерних бандлів.

Дія: перемістіть створення кореня, яке працює лише в браузері, у клієнтську точку входу. Якщо ви використовуєте фреймворк, дотримуйтесь його задокументованої межі сервер/клієнт, а не викликайте createRoot() вручну зі спільного серверного коду.

Таблиця швидкої діагностики

Що ви бачитеЙмовірна причинаНайкраща наступна перевірка
console.log(container) є nullНевідповідність ID або елемент ще не присутнійПорівняйте ID в HTML і пошук; перевірте час виконання скрипту
HTML використовує id="app", код шукає rootНевідповідність ID монтуванняЗробіть обидві назви ідентичними
createRoot(<App />)React-елемент передано там, де потрібен вузол DOMПередайте вузол DOM у createRoot, потім відобразіть <App />
Проєкт досі використовує ReactDOM.render після оновлення до React 18Застарілий клієнтський APIМігруйте на createRoot, використовуючи посібник з оновлення до React 18
Контейнер уже містить HTML React із серверним рендерингомНеправильний API ініціалізації клієнтаВикористовуйте hydrateRoot
Не працює лише ціль модального вікна/підказкиВідсутня ціль портали або зайвий додатковий коріньВикористовуйте createPortal з наявним вузлом DOM
Не проходять лише тестиТестовий DOM ніколи не створював цільовий елементСтворіть контейнер у налаштуваннях тесту або використовуйте рендерер тестової бібліотеки

Фінальна перевірка: підтвердьте виправлення, а не приховуйте помилку

Після внесення змін перевірте шлях запуску в такому порядку:

  1. Відкрийте сторінку та перевірте консоль браузера. Помилка цільового контейнера повинна зникнути.
  2. Виконайте console.log(document.getElementById('root')) і переконайтеся, що вона виводить реальний елемент, а не null.
  3. Переконайтеся, що ви імпортуєте createRoot з react-dom/client у додатку React 18 із клієнтським рендерингом.
  4. Переконайтеся, що елемент DOM передано у createRoot(), а React-компонент передано у root.render().
  5. Якщо сторінка була відображена React на сервері, переконайтеся, що клієнт використовує hydrateRoot() замість цього.
  6. Якщо місцем призначення, що не працює, є модальне вікно або підказка, переконайтеся, що ціль портали існує до виклику createPortal().

Не вважайте твердження non-null у TypeScript, опціональний ланцюжок або блок catch самим по собі виправленням. Ці техніки можуть приглушити шлях помилки, не надавши вузол DOM, який насправді потрібен React. Стійке виправлення полягає в тому, щоб узгодити структуру сторінки, API ініціалізації та час виконання.

Для звичайного односторінкового додатка React 18 найкоротша правильна ментальна модель така: HTML створює контейнер; JavaScript знаходить цей контейнер; createRoot приймає контейнер; root.render приймає компонент. Коли ці чотири частини знаходяться в правильному порядку, помилка «Target container is not a DOM element» зазвичай зникає з правильної причини.

Залишити коментар

Як виправити помилку “Prisma Client Has Not Been Generated Yet”

Як виправити помилку “Prisma Client Has Not Been Generated Yet”

Виправте помилку незгенерованого Prisma Client, перевіривши генератор, схему, шлях виводу, імпорти, версії, налаштування монорепозиторію та кроки збірки під час розгортання.

Як виправити помилку SSL-сертифіката: не вдалося отримати локальний сертифікат емітента в Git

Як виправити помилку SSL-сертифіката: не вдалося отримати локальний сертифікат емітента в Git

Виправте помилку Git «не вдалося отримати локальний сертифікат емітента», визначивши механізм довіри, встановивши правильний ланцюжок ЦС та зберігаючи перевірку SSL увімкненою.

Як виправити помилку таймауту мережі MongoDB у з'єднанні Mongoose

Як виправити помилку таймауту мережі MongoDB у з'єднанні Mongoose

Виправте помилки таймауту мережі MongoDB у Mongoose, визначивши тип таймауту, перевіривши доступність Atlas або TCP, виправивши URI та налаштувавши таймаути лише за необхідності.

Як виправити помилку «Execution Policy Restricted» у Windows PowerShell

Як виправити помилку «Execution Policy Restricted» у Windows PowerShell

Виправте помилку обмеженої політики виконання PowerShell, перевіривши область дії та групову політику, а потім обравши RemoteSigned, Unblock-File або тимчасовий параметр сесії.

Як виправити помилку npm ERR! code ERESOLVE: конфлікт залежностей-партнерів

Як виправити помилку npm ERR! code ERESOLVE: конфлікт залежностей-партнерів

Виправте конфлікти залежностей-партнерів npm ERESOLVE, визначивши несумісний діапазон пакетів, узгодивши версії, використовуючи npm explain та npm ls, а також розглядаючи legacy-peer-deps або force лише як контрольовані резервні варіанти.

Як виправити помилку підключення Redis до 127.0.0.1:6379

Як виправити помилку підключення Redis до 127.0.0.1:6379

Виправте помилки відмови у підключенні Redis на 127.0.0.1:6379, перевіривши сервер, порт, мережу Docker, redis.conf, автентифікацію та TLS.

Як виправити внутрішню помилку 500 у серверних компонентах Next.js

Як виправити внутрішню помилку 500 у серверних компонентах Next.js

Виправте помилки 500 у серверних компонентах Next.js, аналізуючи логи сервера, перевіряючи запити даних та змінні середовища, обробляючи помилки та перевіряючи збірку для продакшену.

Як виправити помилку CrashLoopBackOff у Kubernetes у локальному Minikube

Як виправити помилку CrashLoopBackOff у Kubernetes у локальному Minikube

Діагностуйте та виправляйте помилку CrashLoopBackOff у Kubernetes у локальному Minikube, перевіряючи стан пода, попередні логи, причини завершення роботи, проби, конфігурацію, ліміти пам’яті та стан кластера.

Як виправити помилку «Docker Desktop Engine Stopped» у Windows 11

Як виправити помилку «Docker Desktop Engine Stopped» у Windows 11

Виправте помилку «Docker Desktop Engine Stopped» у Windows 11, перевіривши статус Docker, оновивши та перезавантаживши WSL 2, підтвердивши віртуалізацію та використавши діагностику перед скиданням налаштувань.

Як виправити помилку Uncaught ReferenceError: process is not defined у Vite

Як виправити помилку Uncaught ReferenceError: process is not defined у Vite

Виправте помилку 'process is not defined' у Vite, замінивши використання process.env у стилі Node.js, правильно налаштувавши змінні VITE_ та перевіривши залежності.