Početna
» Osnovno znanje
»
Kako riješiti grešku "Target container is not a DOM element" u React 18
Kako riješiti grešku "Target container is not a DOM element" u React 18
Najvažnija ispravka je sljedeća: provjerite je li vrijednost koju prosljeđujete funkciji createRoot() stvarni DOM element koji već postoji. U Reactu 18, uobičajena klijentska ulazna točka izgleda ovako:
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 />);
Ako document.getElementById('root') vrati null, ili ako slučajno prosljedite React element poput <App /> funkciji createRoot(), React ne može stvoriti korijen i može prijaviti grešku “Target container is not a DOM element.” Reactova vlastita dokumentacija za rješavanje problema s createRoot definira grešku upravo tim riječima: vrijednost prosljeđena funkciji createRoot nije DOM čvor.
Ovaj vodič počinje s tom najvjerojatnijom uzročnom, a zatim pokriva probleme s tajmingom, pogreške pri migraciji na React 18, server-side rendering, portale, TypeScript i testna okruženja, kako biste mogli prestati čitati kada pronađete slučaj koji odgovara vašoj aplikaciji.
Korak 1: Dokažite što zapravo prosljeđujete funkciji createRoot()
Prije promjene konfiguracije, zabilježite sadržaj spremnika (containera):
Ako konzola ispiše null, React nije dio koji je prvi zakažio. Preglednik nije pronašao element s tim ID-om u trenutku kada je vaš kod izvršen. Službeni odjeljak za rješavanje problema u Reactu navodi nepodudaranje ID-a i izvršavanje prije nego što DOM čvor postoji kao česte razloge.
Ako konzola ispiše nešto poput <div id="root"></div>, tada spremnik postoji i trebali biste preskočiti na provjere React API-ja, SSR-a, portala ili okruženja u nastavku.
Primjenjuje se kada: greška se pojavljuje odmah tijekom pokretanja aplikacije, posebno u main.jsx, index.jsx ili index.tsx.
Akcija: nemojte nagađati. Zabilježite točnu vrijednost prosljeđenu funkciji createRoot(). Ako je null, riješite zašto je pretraga DOM-a uspjela prije nego što dirate kod komponenti.
Ilustracija generirana AI-jem koja prikazuje grešku spremnika u Reactu 18 u konzoli programera. To nije snimka zaslona iz stvarne aplikacije ili React DevToolsa.
Korak 2: Uskladite HTML ID s JavaScript pretragom
Najjednostavniji uzrok u stvarnom svijetu je nepodudaranje između vašeg HTML-a i vašeg JavaScripta.
Vaš HTML bi mogao sadržavati:
<div id="app"></div>
dok vaša React ulazna datoteka traži:
document.getElementById('root')
Ta imena se moraju podudarati. Ili promijenite markup:
<div id="root"></div>
ili promijenite pretragu:
const container = document.getElementById('app');
Trenutna createRoot referenca u Reactu koristi standardni primjer document.getElementById('root'), ali root nije magični obavezni ID. Bilo koji stvarni DOM element može se koristiti kao korijenski spremnik. Važan uvjet je da element postoji i da ga pretraga vraća.
Primjenjuje se kada: nedavno ste promijenili HTML predložak, migrirali s Create React Appa na Vite ili drugi bundler, ugradili React u postojeću stranicu renderiranu na serveru ili preimenovali element za montiranje.
Akcija: pretražite projekt za oba pojma: id="root" i getElementById('root'). Ako projekt namjerno koristi drugi ID, uskladite HTML i JavaScript.
Ilustracija generirana AI-jem koja prikazuje odgovarajući element za montiranje id="root". To je konceptualni prikaz uređivača koda, a ne snimka zaslona određenog predloška okvira.
Korak 3: Koristite React 18 root API u ispravnom redoslijedu
React 18 uveo je klijentski API createRoot. Službeni vodič za nadogradnju na React 18 prikazuje migraciju sa starijeg obrasca ReactDOM.render na:
Iznenađujuće laka pogreška je zamjena uloga DOM spremnika i React komponente:
// Pogrešno
createRoot(<App />);
React eksplicitno navodi ovo kao još jedan čest uzrok greške “Target container is not a DOM element”. Funkcija createRoot() prima DOM čvor; funkcija root.render() prima React čvor.
Još jedna pogreška pri migraciji je mentalno prenošenje React 17 potpisa i pokušaj prosljeđivanja spremnika funkciji root.render():
Reactova createRoot referenca dokumentira root.render(reactNode) kao funkciju koja prima React čvor, dok spremnik pripada funkciji createRoot(domNode).
TypeScript: nemojte brkati ne-null aserciju s ispravkom u vrijeme izvršavanja
Vodič za nadogradnju na React 18 prikazuje createRoot(container!) kao TypeScript oblik. Uskličnik je asercija u vrijeme kompilacije: govori TypeScriptu da vjerujete da vrijednost nije null. On ne stvara nedostajući HTML element u vrijeme izvršavanja.
Sigurniji obrazac kada debugirate je:
const container = document.getElementById('root');
if (container === null) {
throw new Error('Expected #root to exist');
}
createRoot(container).render(<App />);
Ovo proizvodi korisniju grešku specifičnu za aplikaciju ako se HTML i JavaScript razdvoje.
Primjenjuje se kada: problem se pojavio tijekom migracije s Reacta 17 na React 18, nakon kopiranja ulazne datoteke iz drugog projekta, ili samo u TypeScript buildovima gdje je dodan ! kako bi se utišalo upozorenje kompilatora.
Akcija: provjerite slijed poziva: DOM lookup → createRoot(container) → root.render(<App />). Tijekom debugiranja, dajte prednost eksplicitnoj provjeri null vrijednosti umjesto slijepog asertiranja container!.
Ilustracija generirana AI-jem koja prikazuje obrambeni obrazac pokretanja Reacta 18. Kod je prikazan kao konceptualni primjer, a ne kao uhvaćeni izlaz iz stvarnog projekta.
Korak 4: Provjerite pokreće li se vaš kod za pokretanje nakon što ciljni element postoji
ID može biti savršeno napisan i ipak vratiti null ako se vaša skripta pokrene prije nego što je preglednik parsirao element. Reactova dokumentacija za rješavanje problema posebno upozorava da bundle skripta ne može vidjeti DOM čvorove koji se pojavljuju kasnije u HTML-u kada se izvršavanje dogodi prerano.
Ovo je uglavnom relevantno za prilagođene HTML stranice i starije postavke ugradnje. Uobičajeni siguran raspored je stavljanje elementa za montiranje prije skripte:
Ako kontrolirate prilagođenu skriptu koja se može pokrenuti prije završetka parsiranja, druga obrambena opcija je čekanje na 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();
}
Nemojte automatski dodavati ovaj omotač svakom React projektu. Moderni bundleri i okviri obično upravljaju postavljanjem ulazne skripte i semantikom učitavanja umjesto vas. Ako standardna Vite, Next.js, Remix ili aplikacija generirana okvirom iznenada razvije ovu grešku, prvo potražite promijenjeni predložak, ID montiranja, prilagođenu integraciju ili kod koji se izvršava izvan očekivane klijentske ulazne točke.
Primjenjuje se kada: isti ID postoji u konačnom HTML-u, ali pretraga je i dalje null tijekom pokretanja, posebno u ručno sastavljenoj HTML stranici, CMS predlošku, ugrađenom widgetu ili integraciji skripte treće strane.
Akcija: pregledajte stvarni izvor stranice i redoslijed izvršavanja. Osigurajte da čvor za montiranje postoji prije koda koji poziva createRoot().
Ilustracija generirana AI-jem koja prikazuje zaštitu spremnosti DOM-a za prilagođeno React pokretanje. To nije obavezni obrazac za svaku React 18 aplikaciju; koristite ga samo kada je tajming pokretanja stvarno problem.
Ako je vaša stranica renderirana na serveru, koristite hydrateRoot umjesto toga
Postoji važan uvjet gdje valjani DOM element nije dovoljan da createRoot() bude ispravan API. Ako spremnik već sadrži HTML generiran Reactom na serveru ili u vrijeme builda, Reactova dokumentacija kaže da koristite hydrateRoot() umjesto 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 />);
Razlog je drugačiji od greške ciljnog spremnika. createRoot() upravlja klijentski renderiranim korijenom i, pri prvom renderiranju, briše postojeći HTML unutar tog korijena. hydrateRoot() pričvršćuje React na HTML koji je već proizveo React na serveru. Službena createRoot dokumentacija eksplicitno ističe ovo kao zamku server-side renderiranja.
Primjenjuje se kada: imate React HTML renderiran na serveru, statičku generaciju koja emitira React markup ili okvir koji hidratizira HTML u pregledniku.
Akcija: nemojte “popravljati” SSR zamjenom server markupa praznim spremnikom. Koristite hidratacijsku ulaznu točku okvira ili Reactov hydrateRoot() prema potrebi.
Ako je ovo modal ili ciljni element tooltipa, možda vam treba createPortal – a ne drugi korijen
Ponekad programeri vide nedostajući spremnik dok pokušavaju renderirati modal, tooltip, regiju toast obavijesti ili overlay izvan glavne stabla aplikacije. Reactova dokumentacija kaže da kada želite da se JSX pojavi drugdje u DOM-u, koristite createPortal() umjesto stvaranja novog korijena samo za to dijete UI-a.
import { createPortal } from 'react-dom';
function Modal({ children }) {
const modalRoot = document.getElementById('modal-root');
if (!modalRoot) return null;
return createPortal(children, modalRoot);
}
Službena createPortal dokumentacija kaže da ciljni element portala mora već postojati. Dakle, portali mogu proizvesti sličnu klasu problema sa spremnikom ako modal-root nedostaje, ali arhitektonska ispravka nije nužno “pozvati createRoot ponovno”.
Primjenjuje se kada: neuspjeli spremnik nije glavni korijen vaše aplikacije, već odredište overlaya ili čvor upravljan izvan normalne DOM pozicije komponente.
Akcija: zadržite jedan normalni korijen aplikacije osim ako zaista ne trebate više neovisnih korijena. Za UI u stilu modala unutar iste React aplikacije, dajte prednost portalu na postojeći DOM čvor.
Što ako se greška događa samo u testovima?
Test može zakažiti iz istog temeljnog razloga: očekivani spremnik nikada nije umetnut u testni DOM. Ako vaš test ručno poziva createRoot(document.getElementById('root')), provjerite postavljaju li testovi taj čvor prije renderiranja.
Međutim, mnoge React testne biblioteke upravljaju spremnicima umjesto vas. Ako već koristite render() pomoćnu funkciju testnog okvira, ručno stvaranje React korijena možda je nepotrebno i može učiniti postavke testova krhkijima.
Primjenjuje se kada: razvoj funkcionira u pregledniku, ali Jest, Vitest, JSDOM ili drugo testno okruženje baca grešku spremnika.
Akcija: pregledajte DOM postavke testa, a ne produkcijski index.html. Potvrdite da čvor postoji unutar okruženja u kojem se neuspješan kod stvarno izvršava.
Što ako dokument nije dostupan?
Ako kod koji poziva document.getElementById() izvršava se na serveru ili u drugom ne-pregledničkom okruženju, imate drugačiji problem s integracijom. Reactovi klijentski API-ji u react-dom/client dizajnirani su za renderiranje u DOM čvorove preglednika. Server-side renderiranje koristi API-je iz react-dom/server, a okviri obično odvajaju server i klijentske ulazne točke.
Primjenjuje se kada: greška se pojavljuje tijekom server-side renderiranja, Node build koraka ili koda dijeljenog između server i klijentskih bundleova.
Akcija: premjestite kreiranje korijena samo za preglednik u klijentsku ulaznu točku. Ako koristite okvir, slijedite njegovu dokumentiranu granicu server/klijent umjesto ručnog pozivanja createRoot() iz dijeljenog server koda.
Tablica brze dijagnostike
Što vidite
Vjerojatni uzrok
Najbolja sljedeća provjera
console.log(container) je null
Nepodudaranje ID-a ili element još nije prisutan
Usporedite HTML ID i pretragu; pregledajte tajming skripti
HTML koristi id="app", kod upituje root
Nepodudaranje ID-a za montiranje
Učinite oba imena identičnima
createRoot(<App />)
React element prosljeđen tamo gdje je potreban DOM čvor
Prosljedite DOM čvor funkciji createRoot, zatim renderirajte <App />
Projekt i dalje koristi ReactDOM.render nakon nadogradnje na React 18
Legacy klijentski API
Migrirajte na createRoot koristeći vodič za nadogradnju na React 18
Spremnik već sadrži React HTML renderiran na serveru
Pogrešan API za inicijalizaciju klijenta
Koristite hydrateRoot
Samo modal/tooltip cilj ne uspijeva
Nedostajući cilj portala ili nepotrebni dodatni korijen
Koristite createPortal s postojećim DOM čvorom
Samo testovi ne uspijevaju
Testni DOM nikada nije stvorio ciljni element
Stvorite spremnik u postavkama testa ili koristite renderer testne biblioteke
Konačna provjera: potvrdite ispravku umjesto skrivanja greške
Nakon što napravite promjenu, provjerite putanju pokretanja ovim redoslijedom:
Otvorite stranicu i pregledajte konzolu preglednika. Greška ciljnog spremnika bi trebala nestati.
Pokrenite console.log(document.getElementById('root')) i potvrdite da ispisuje stvarni element, a ne null.
Potvrdite da uvozite createRoot iz react-dom/client u React 18 aplikaciji renderiranoj na klijentu.
Potvrdite da je DOM element prosljeđen funkciji createRoot() i da je React komponenta prosljeđena funkciji root.render().
Ako je stranicu renderirao React na serveru, potvrdite da klijent koristi hydrateRoot() umjesto toga.
Ako je neuspjelo odredište modal ili tooltip, potvrdite da cilj portala postoji prije pozivanja createPortal().
Nemojte smatrati TypeScript ne-null aserciju, opcionalno lančanje ili catch blok samom ispravkom. Te tehnike mogu utišati putanju greške bez pružanja DOM čvora koji React stvarno treba. Trajna ispravka je usklađivanje strukture stranice, API-ja za inicijalizaciju i tajminga izvršavanja.
Za normalnu React 18 single-page aplikaciju, najkraći ispravan mentalni model je: HTML stvara spremnik; JavaScript pronalazi taj spremnik; createRoot prima spremnik; root.render prima komponentu. Kada su ta četiri dijela u ispravnom redoslijedu, greška “Target container is not a DOM element” obično nestaje iz ispravnog razloga.