Kaip ištaisyti klaidą „Target container is not a DOM element“ React 18
Svarbiausias sprendimas yra šis: įsitikinkite, kad į createRoot() perduodama reikšmė yra tikras DOM elementas, kuris jau egzistuoja. React 18 įprasta kliento pradinė vieta atrodo taip:
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 />);
Jei document.getElementById('root') grąžina null, arba jei netyčia perduodate React elementą, pvz., <App />, į createRoot(), React negali sukurti šaknies ir gali pranešti apie klaidą „Target container is not a DOM element“. React createRoot trikčių šalinimo dokumentacija apibrėžia šią klaidą būtent taip: į createRoot perduota reikšmė nėra DOM mazgas.
Šis vadovas prasideda nuo šios didelės tikimybės priežasties, tada aptariami laiko problemos, React 18 migracijos klaidos, serverio atvaizdavimas, portalai, TypeScript ir testavimo aplinkos, kad galėtumėte sustoti, kai rasite atvejį, atitinkantį jūsų programą.
1 žingsnis: Įrodykite, ką iš tikrųjų perduodate į createRoot()
Prieš keisdami konfigūraciją, užregistruokite konteinerį:
Jei konsolėje atspausdinama null, React nėra ta dalis, kuri pirmiausia nepavyko. Naršyklė nerado elemento su tuo ID tuo metu, kai vyko jūsų kodas. Oficiali React trikčių šalinimo skiltis nurodo ID neatitikimą ir vykdymą prieš egzistuojant DOM mazgui kaip dažnas priežastis.
Jei konsolėje atspausdinama kažkas panašaus į <div id="root"></div>, tuomet konteineris egzistuoja ir turėtumėte pereiti prie žemiau pateiktų React API, SSR, portalo ar aplinkos patikrinimų.
Taikoma, kai: klaida atsiranda iškart paleidus programą, ypač main.jsx, index.jsx arba index.tsx.
Veiksmas: nespėliokite. Užregistruokite tikslią reikšmę, perduodamą į createRoot(). Jei ji yra null, ištaisykite, kodėl DOM paieška nepavyko, prieš liesdami komponento kodą.
Dirbtiniu intelektu sugeneruota React 18 konteinerio klaidos iliustracija kūrėjo konsolėje. Tai nėra tikros programos ar React DevTools ekrano kopija.
2 žingsnis: Suderinkite HTML ID su JavaScript paieška
Paprasta reali priežastis yra neatitikimas tarp jūsų HTML ir JavaScript.
Jūsų HTML gali būti:
<div id="app"></div>
o jūsų React pradinis failas prašo:
document.getElementById('root')
Šie pavadinimai turi sutapti. Pakeiskite žymėjimą:
<div id="root"></div>
arba pakeiskite paiešką:
const container = document.getElementById('app');
React dabartinė createRoot nuoroda naudoja standartinį pavyzdį document.getElementById('root'), tačiau root nėra magiškas privalomas ID. Bet kuris tikras DOM elementas gali būti naudojamas kaip šakninis konteineris. Svarbi sąlyga yra ta, kad elementas egzistuotų ir paieška jį grąžintų.
Taikoma, kai: neseniai keitėte HTML šabloną, migruojate iš Create React App į Vite ar kitą pakuočių rinktuvą, integruojate React į esamą serverio atvaizduotą puslapį arba pervadinote montavimo elementą.
Veiksmas: ieškokite projekte tiek id="root", tiek getElementById('root'). Jei projektas sąmoningai naudoja kitą ID, suderinkite HTML ir JavaScript.
Dirbtiniu intelektu sugeneruota atitinkančio id="root" montavimo elemento iliustracija. Tai konceptualus kodo redaktoriaus vaizdas, o ne konkretaus karkaso šablono ekrano kopija.
3 žingsnis: Naudokite React 18 šaknies API teisinga tvarka
React 18 pristatė createRoot kliento API. Oficialus React 18 atnaujinimo vadovas rodo migraciją iš senesnio ReactDOM.render modelio į:
Stebėtinai lengva klaida yra supainioti DOM konteinerio ir React komponento vaidmenis:
// Neteisingai
createRoot(<App />);
React aiškiai nurodo tai kaip kitą dažną klaidos „Target container is not a DOM element“ priežastį. createRoot() gauna DOM mazgą; root.render() gauna React mazgą.
Kita migracijos klaida yra protu perkelti React 17 funkcijos parašą ir bandyti perduoti konteinerį į root.render():
React createRoot nuoroda dokumentuoja root.render(reactNode) kaip priimantį React mazgą, o konteineris priklauso createRoot(domNode).
TypeScript: nemaišykite ne-null teiginio su vykdymo laiko pataisymu
React 18 atnaujinimo vadovas rodo createRoot(container!) kaip TypeScript formą. Šauktinis yra kompiliavimo laiko teiginys: jis nurodo TypeScript, kad manote, jog reikšmė nėra null. Jis nesukuria trūkstamo HTML elemento vykdymo metu.
Saugesnis modelis derinant yra:
const container = document.getElementById('root');
if (container === null) {
throw new Error('Expected #root to exist');
}
createRoot(container).render(<App />);
Tai sukuria naudingesnę konkrečiai programai skirtą klaidą, jei HTML ir JavaScript išsiskiria.
Taikoma, kai: problema atsirado migruojant iš React 17 į React 18, nukopijavus pradinį failą iš kito projekto arba tik TypeScript kompiliacijose, kur ! buvo pridėtas norint nutildyti kompiliatoriaus įspėjimą.
Veiksmas: patikrinkite kvietimų seką: DOM paieška → createRoot(container) → root.render(<App />). Derinant metu pirmenybę teikite aiškiam null patikrinimui, o ne aklai teigiant container!.
Dirbtiniu intelektu sugeneruota gynybinio React 18 paleidimo modelio iliustracija. Kodas pateikiamas kaip konceptualus pavyzdys, o ne kaip sugautas išvesties rezultatas iš tikro projekto.
4 žingsnis: Įsitikinkite, kad paleidimo kodas vykdomas po to, kai tikslinis elementas egzistuoja
ID gali būti tobulai parašytas ir vis tiek grąžinti null, jei jūsų skriptas veikia prieš naršyklei išanalizavus elementą. React trikčių šalinimo dokumentai konkrečiai perspėja, kad paketo skriptas nemato DOM mazgų, kurie atsiranda vėliau HTML, kai vykdymas įvyksta per anksti.
Tai daugiausia susiję su tinkintais HTML puslapiais ir senesnėmis integravimo sąrankomis. Dažnas saugus išdėstymas yra pastatyti montavimo elementą prieš skriptą:
Jei kontroliuojate tinkintą skriptą, kuris gali veikti prieš baigiantis analizavimui, kita gynybinė parinktis yra laukti 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();
}
Neprievirkite šio apvalkalo automatiškai kiekvienam React projektui. Šiuolaikiniai pakuočių rinktuvai ir karkasai paprastai valdo pradinio skripto vietą ir įkėlimo semantiką už jus. Jei standartinė Vite, Next.js, Remix ar karkaso sugeneruota programa staiga pradeda rodyti šią klaidą, pirmiausia ieškokite pakeisto šablono, montavimo ID, tinkintos integracijos arba kodo, veikiančio už tikėtinos naršyklės pradinės vietos.
Taikoma, kai: tas pats ID egzistuoja galutiniame HTML, tačiau paieška paleidimo metu vis tiek yra null, ypač rankiniu būdu surinktuose HTML puslapiuose, CMS šablonuose, valdiklių įterpimuose ar trečiųjų šalių skriptų integracijose.
Veiksmas: apžiūrėkite tikrąjį puslapio šaltinį ir vykdymo tvarką. Įsitikinkite, kad montavimo mazgas egzistuoja prieš kodą, kuris kviečia createRoot().
Dirbtiniu intelektu sugeneruota DOM paruošimo apsaugos iliustracija tinkintam React paleidimui. Tai nėra privalomas modelis kiekvienai React 18 programai; naudokite jį tik tada, kai paleidimo laikas iš tikrųjų yra problema.
Jei jūsų puslapis atvaizduojamas serveryje, naudokite hydrateRoot vietoj to
Yra svarbi sąlyga, kai galiojantis DOM elementas nėra pakankamas, kad createRoot() būtų teisingas API. Jei konteineris jau turi HTML, kurį sugeneravo React serveryje arba kūrimo metu, React dokumentacija nurodo naudoti hydrateRoot() vietoj 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 />);
Priežastis skiriasi nuo tikslinio konteinerio klaidos. createRoot() valdo kliento atvaizduotą šaknį ir pirmojo atvaizdavimo metu išvalo esamą HTML toje šaknyje. hydrateRoot() prijungia React prie HTML, kurį serveryje jau sukūrė React. Oficiali createRoot dokumentacija tai aiškiai nurodo kaip serverio atvaizdavimo spąstą.
Taikoma, kai: turite serveryje atvaizduotą React HTML, statinį generavimą, kuris išskiria React žymėjimą, arba karkasą, kuris hidrata HTML naršyklėje.
Veiksmas: ne „taisykite“ SSR pakeisdami serverio žymėjimą tuščiu konteineriu. Naudokite karkaso hidratacijos pradinę vietą arba React hydrateRoot(), jei tinkama.
Jei tai modalo ar patarimo tikslas, gali prireikti createPortal, o ne kitos šaknies
Kartais kūrėjai mato trūkstamą konteinerį bandydami atvaizduoti modalą, patarimą, pranešimų sritį ar perdangą už pagrindinės programos medžio. React dokumentai teigia, kad norint, jog JSX atsirastų kitoje DOM vietoje, naudokite createPortal(), o ne kurkite kitą šaknį tik tam vaikų UI.
import { createPortal } from 'react-dom';
function Modal({ children }) {
const modalRoot = document.getElementById('modal-root');
if (!modalRoot) return null;
return createPortal(children, modalRoot);
}
Oficiali createPortal dokumentacija teigia, kad portalo tikslas turi jau egzistuoti. Taigi portalai gali sukelti susijusią konteinerio problemų klasę, jei modal-root trūksta, tačiau architekūrinis sprendimas nebūtinai yra „vėl iškviesti createRoot“.
Taikoma, kai: nepavykstantis konteineris nėra jūsų programos pagrindinė šaknis, o perdangos tikslas arba mazgas, valdomas už komponento įprastos DOM padėties.
Veiksmas: laikykitės vienos įprastos programos šaknies, nebent jums iš tikrųjų reikia kelių nepriklausomų šaknų. Modalinio tipo UI toje pačioje React programoje pirmenybę teikite portalui į esamą DOM mazgą.
Ką daryti, jei klaida kyla tik testuose?
Testas gali nepavykti dėl tos pačios esminės priežasties: tikėtinas konteineris niekada nebuvo įterptas į testavimo DOM. Jei jūsų testas rankiniu būdu kviečia createRoot(document.getElementById('root')), įsitikinkite, kad testavimo sąranka iš tikrųjų sukuria tą mazgą prieš atvaizduojant.
Tačiau daugelis React testavimo bibliotekų valdo konteinerius už jus. Jei jau naudojate testavimo karkaso render() pagalbą, rankinis React šaknies kūrimas gali būti nereikalingas ir padaryti testavimo sąranką trapesnę.
Taikoma, kai: kūrimas veikia naršyklėje, tačiau Jest, Vitest, JSDOM ar kita testavimo aplinka meta konteinerio klaidą.
Veiksmas: apžiūrėkite testo DOM sąranką, o ne gamybos index.html. Patvirtinkite, kad mazgas egzistuoja aplinkoje, kurioje iš tikrųjų veikia nepavykęs kodas.
Ką daryti, jei dokumentas neprieinamas?
Jei kodas, kviečiantis document.getElementById(), vykdomas serveryje ar kitoje ne naršyklės aplinkoje, turite kitokią integracijos problemą. React kliento API react-dom/client yra sukurti atvaizduoti į naršyklės DOM mazgus. Serverio atvaizdavimas naudoja API iš react-dom/server, o karkasai paprastai atskiria serverio ir kliento pradines vietas.
Taikoma, kai: klaida atsiranda serverio pusės atvaizdavimo metu, Node kūrimo žingsnyje arba kode, bendrinamame tarp serverio ir naršyklės paketų.
Veiksmas: perkelti tik naršyklei skirtą šaknies kūrimą į kliento pradinę vietą. Jei naudojate karkasą, laikykitės jo dokumentuotos kliento/serverio ribos, o ne rankiniu būdu kvieskite createRoot() iš bendro serverio kodo.
Greitos diagnostikos lentelė
Ką matote
Tikėtina priežastis
Geriausias kitas patikrinimas
console.log(container) yra null
ID neatitikimas arba elementas dar nėra
Palyginkite HTML ID ir paiešką; apžiūrėkite skripto laiką
HTML naudoja id="app", kodas užklauso root
Montavimo ID neatitikimas
Suderinkite abu pavadinimus
createRoot(<App />)
React elementas perduotas ten, kur reikalingas DOM mazgas
Perduokite DOM mazgą į createRoot, tada atvaizduokite <App />
Projektas vis dar naudoja ReactDOM.render po React 18 atnaujinimo
Paveldėtas kliento API
Migruokite į createRoot naudodami React 18 atnaujinimo vadovą
Konteineryje jau yra serveryje atvaizduoto React HTML
Neteisingas kliento inicializavimo API
Naudokite hydrateRoot
Nepavyksta tik modalas/patarimo tikslas
Trūkstamas portalo tikslas arba nereikalinga papildoma šaknis
Naudokite createPortal su esamu DOM mazgu
Nepavyksta tik testai
Testavimo DOM niekada nesukūrė tikslinio elemento
Sukurkite konteinerį testų sąrankoje arba naudokite testų bibliotekos atvaizduotuvą
Galutinis patikrinimas: patvirtinkite pataisymą, o ne paslėpkite klaidą
Po pakeitimo patvirtinkite paleidimo kelią šia tvarka:
Atidarykite puslapį ir apžiūrėkite naršyklės konsolę. Tikslinio konteinerio klaida turėtų dingti.
Vykdykite console.log(document.getElementById('root')) ir patvirtinkite, kad atspausdinamas tikras elementas, o ne null.
Patvirtinkite, kad importuojate createRoot iš react-dom/client React 18 kliento atvaizduotoje programoje.
Patvirtinkite, kad DOM elementas perduodamas į createRoot(), o React komponentas perduodamas į root.render().
Jei puslapį serveryje atvaizdavo React, patvirtinkite, kad klientas vietoj to naudoja hydrateRoot().
Jei nepavykstantis tikslas yra modalas ar patarimas, patvirtinkite, kad portalo tikslas egzistuoja prieš kviečiant createPortal().
Nelaikykite TypeScript ne-null teiginio, pasirinktinės grandinės ar catch bloko pačiu savaime sprendimu. Šios technikos gali nutildyti klaidos kelią nepateikdamos DOM mazgo, kurio React iš tikrųjų reikia. Ilgalaikis sprendimas yra suderinti puslapio struktūrą, inicializavimo API ir vykdymo laiką.
Įprastai React 18 vieno puslapio programai trumpiausias teisingas protinis modelis yra: HTML sukuria konteinerį; JavaScript randa tą konteinerį; createRoot priima konteinerį; root.render priima komponentą. Kai keturios dalys yra teisinga tvarka, „Target container is not a DOM element“ paprastai dingsta dėl teisingos priežasties.