| title | createRoot |
|---|
createRootλ‘ λΈλΌμ°μ DOM λ
Έλ μμ React μ»΄ν¬λνΈλ₯Ό νμνλ 루νΈλ₯Ό μμ±ν μ μμ΅λλ€.
const root = createRoot(domNode, options?)createRootλ₯Ό νΈμΆνλ©΄ λΈλΌμ°μ DOM μλ¦¬λ¨ΌνΈ μμ μ½ν
μΈ λ₯Ό νμν μ μλ React 루νΈλ₯Ό μμ±ν©λλ€.
import { createRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = createRoot(domNode);Reactλ domNodeμ λν 루νΈλ₯Ό μμ±νκ³ κ·Έ μμ μλ DOMμ κ΄λ¦¬ν©λλ€. 루νΈλ₯Ό μμ±ν νμλ root.renderλ₯Ό νΈμΆν΄ κ·Έ μμ React μ»΄ν¬λνΈλ₯Ό νμν΄μΌ ν©λλ€.
root.render(<App />);μ¨μ ν Reactλ§μΌλ‘ μμ±λ μ±μμλ μΌλ°μ μΌλ‘ λ£¨νΈ μ»΄ν¬λνΈμ λν createRoot νΈμΆμ΄ νλλ§ μμ΅λλ€. νμ΄μ§μ μΌλΆμ Reactλ₯Ό "λΏλ €μ" μ¬μ©νλ νμ΄μ§μ κ²½μ°μλ 루νΈλ₯Ό νμλ‘ νλ λ§νΌ μμ±ν μ μμ΅λλ€.
μλ μμλ₯Ό μ°Έκ³ νμΈμ.
-
domNode: DOM μ리먼νΈ. Reactλ DOM μ리먼νΈμ λν 루νΈλ₯Ό μμ±νκ³ λ λλ§λ React μ½ν μΈ λ₯Ό νμνλrenderμ κ°μ ν¨μλ₯Ό 루νΈμμ νΈμΆν μ μλλ‘ ν©λλ€. -
optional
options: React 루νΈμ λν μ΅μ μ κ°μ§ κ°μ²΄μ λλ€.- optional
onCaughtError: Reactκ° Error Boundaryμμ μ€λ₯λ₯Ό μ‘μ λ νΈμΆλλ μ½λ°±. Error Boundaryμμ μ‘μerrorμcomponentStackμ ν¬ν¨νλerrorInfoκ°μ²΄μ ν¨κ» νΈμΆλ©λλ€. - optional
onUncaughtError: μ€λ₯κ° Error Boundaryμ μν΄ μ‘νμ§ μμ λ νΈμΆλλ μ½λ°±. μ€λ₯κ° λ°μνerrorμcomponentStackμ ν¬ν¨νλerrorInfoκ°μ²΄μ ν¨κ» νΈμΆλ©λλ€. - optional
onRecoverableError: Reactκ° μ€λ₯λ‘λΆν° μλμΌλ‘ 볡ꡬλ λ νΈμΆλλ μ½λ°±. Reactκ° λμ§λerrorμcomponentStackμ ν¬ν¨νλerrorInfoκ°μ²΄μ ν¨κ» νΈμΆλ©λλ€. 볡ꡬ κ°λ₯ν μ€λ₯λ μλ³Έ μ€λ₯ μμΈμerror.causeλ‘ ν¬ν¨ν μ μμ΅λλ€. - optional
identifierPrefix: Reactκ°useIdμ μν΄ μμ±λ IDμ μ¬μ©νλ λ¬Έμμ΄ μ λμ¬. κ°μ νμ΄μ§μμ μ¬λ¬κ°μ 루νΈλ₯Ό μ¬μ©ν λ μΆ©λμ νΌνλ λ° μ μ©ν©λλ€.
- optional
createRootλ renderμ unmount λ κ°μ§ λ©μλλ₯Ό ν¬ν¨ν κ°μ²΄λ₯Ό λ°νν©λλ€.
- μ±μ΄ μλ²μμ λ λλ§ λλ κ²½μ°
createRoot()λ μ¬μ©ν μ μμ΅λλ€. λμhydrateRoot()λ₯Ό μ¬μ©νμΈμ. - μ±μμ
createRootνΈμΆμ΄ λ¨ νλ²λ§ μμ κ°λ₯μ±μ΄ λμ΅λλ€. νλ μμν¬λ₯Ό μ¬μ©νλ κ²½μ° νλ μμν¬κ° μ΄ νΈμΆμ λμ μνν μλ μμ΅λλ€. - μ»΄ν¬λνΈμ μμμ΄ μλ DOM νΈλ¦¬μ λ€λ₯Έ λΆλΆ(μ: λͺ¨λ¬ λλ ν΄ν)μ JSX μ‘°κ°μ λ λλ§νλ €λ κ²½μ°,
createRootλμcreatePortalμ μ¬μ©νμΈμ.
root.renderλ₯Ό νΈμΆνμ¬ JSX μ‘°κ°("React λ
Έλ")μ React 루νΈμ λΈλΌμ°μ DOM λ
Έλμ νμν©λλ€.
root.render(<App />);Reactλ rootμ <App />μ νμνκ³ κ·Έ μμ μλ DOMμ κ΄λ¦¬ν©λλ€.
μλ μμλ₯Ό μ°Έκ³ νμΈμ.
reactNode: νμνλ €λ React λ Έλ. μΌλ°μ μΌλ‘<App />κ³Ό κ°μ JSX μ‘°κ°μ΄ λμ§λ§,createElement()λ‘ μμ±ν React μ리먼νΈ, λ¬Έμμ΄, μ«μ,null,undefinedλ±μ μ λ¬ν μλ μμ΅λλ€.
root.renderλ undefinedλ₯Ό λ°νν©λλ€.
-
root.renderλ₯Ό μ²μ νΈμΆνλ©΄ Reactλ React μ»΄ν¬λνΈλ₯Ό λ λλ§νκΈ° μ μ React λ£¨νΈ λ΄λΆμ λͺ¨λ κΈ°μ‘΄ HTML μ½ν μΈ λ₯Ό μ§μλλ€. -
μλ²μμ λλ λΉλ μ€μ Reactμ μν΄ μμ±λ HTMLμ΄ λ£¨νΈμ DOM λ Έλμ ν¬ν¨λ κ²½μ°, λμ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό κΈ°μ‘΄ HTMLμ 첨λΆνλ
hydrateRoot()λ₯Ό μ¬μ©νμΈμ. -
λμΌν 루νΈμμ
renderλ₯Ό λ λ² μ΄μ νΈμΆνλ©΄, Reactλ νμμ λ°λΌ DOMμ μ λ°μ΄νΈνμ¬ μ¬μ©μκ° μ λ¬ν μ΅μ JSXλ₯Ό λ°μν©λλ€. Reactλ μ΄μ μ λ λλ§ λ νΈλ¦¬μ "λΉκ΅"ν΄μ μ¬μ¬μ©ν μ μλ λΆλΆκ³Ό λ€μ λ§λ€μ΄μΌ νλ λΆλΆμ κ²°μ ν©λλ€. λμΌν 루νΈμμrenderλ₯Ό λ€μ νΈμΆνλ κ²μ λ£¨νΈ μ»΄ν¬λνΈμμsetν¨μλ₯Ό νΈμΆνλ κ²κ³Ό λΉμ·ν©λλ€. Reactλ λΆνμν DOM μ λ°μ΄νΈλ₯Ό νΌν©λλ€.
root.unmountλ₯Ό νΈμΆνλ©΄ React λ£¨νΈ λ΄λΆμμ λ λλ§λ νΈλ¦¬λ₯Ό μμ ν©λλ€.
root.unmount();μ¨μ ν Reactλ§μΌλ‘ μμ±λ μ±μλ μΌλ°μ μΌλ‘ root.unmountμ λν νΈμΆμ΄ μμ΅λλ€.
μ΄ ν¨μλ μ£Όλ‘ React 루νΈμ DOM λ
Έλ(λλ κ·Έ μ‘°μ λ
Έλ)κ° λ€λ₯Έ μ½λμ μν΄ DOMμμ μ κ±°λ μ μλ κ²½μ°μ μ μ©ν©λλ€. μλ₯Ό λ€μ΄ DOMμμ λΉνμ± νμ μ κ±°νλ jQuery ν ν¨λμ μμν΄ λ³΄μΈμ. νμ΄ μ κ±°λλ©΄ κ·Έ μμ μλ λͺ¨λ κ²(λ΄λΆμ React 루νΈλ₯Ό ν¬ν¨)μ΄ DOMμμ μ κ±°λ©λλ€. μ΄ κ²½μ° root.unmountλ₯Ό νΈμΆνμ¬ μ κ±°λ 루νΈμ μ½ν
μΈ κ΄λ¦¬λ₯Ό "μ€μ§"νλλ‘ Reactμ μ§μν΄μΌ ν©λλ€. κ·Έλ μ§ μμΌλ©΄ μ κ±°λ λ£¨νΈ λ΄λΆμ μ»΄ν¬λνΈλ ꡬλ
κ³Ό κ°μ μ μ 리μμ€λ₯Ό μ 리νκ³ ν보νλ λ²μ λͺ¨λ₯΄λ μ±λ‘ μκ² λ©λλ€.
root.unmountλ₯Ό νΈμΆνλ©΄ 루νΈμ μλ λͺ¨λ μ»΄ν¬λνΈκ° λ§μ΄νΈ ν΄μ λκ³ , νΈλ¦¬μμ μ΄λ²€νΈ νΈλ€λ¬λ Stateκ° μ κ±°λλ©°, λ£¨νΈ DOM λ
Έλμμ Reactκ° "λΆλ¦¬"λ©λλ€.
root.unmountλ λ§€κ°λ³μλ₯Ό λ°μ§ μμ΅λλ€.
root.unmount returns undefined.
-
root.unmountλ₯Ό νΈμΆνλ©΄ νΈλ¦¬μ λͺ¨λ μ»΄ν¬λνΈκ° λ§μ΄νΈ ν΄μ λκ³ λ£¨νΈ DOM λ Έλμμ Reactκ° "λΆλ¦¬"λ©λλ€. -
root.unmountλ₯Ό ν λ² νΈμΆν νμλ κ°μ 루νΈμμroot.renderλ₯Ό λ€μ νΈμΆν μ μμ΅λλ€. λ§μ΄νΈ ν΄μ λ 루νΈμμroot.renderλ₯Ό νΈμΆνλ €κ³ νλ©΄ "λ§μ΄νΈ ν΄μ λ 루νΈλ₯Ό μ λ°μ΄νΈν μ μμ΅λλ€.Cannot update an unmounted root" μ€λ₯κ° λ°μν©λλ€. κ·Έλ¬λ ν΄λΉ λ Έλμ μ΄μ 루νΈκ° λ§μ΄νΈ ν΄μ λ ν λμΌν DOM λ Έλμ μλ‘μ΄ λ£¨νΈλ₯Ό λ§λ€ μλ μμ΅λλ€.
μ±μ΄ μ¨μ ν Reactλ§μΌλ‘ μμ±λ κ²½μ°, μ 체 μ±μ λν΄ λ¨μΌ 루νΈλ₯Ό μμ±νμΈμ.
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);μΌλ°μ μΌλ‘ μ΄ μ½λλ μμν λ ν λ²λ§ μ€ννλ©΄ λ©λλ€.
- HTMLμ μ μλ λΈλΌμ°μ DOM λ Έλλ₯Ό μ°ΎμΌμΈμ.
- μ± λ΄λΆμ React μ»΄ν¬λνΈλ₯Ό νμνμΈμ.
<!DOCTYPE html>
<html>
<head><title>My app</title></head>
<body>
<!-- This is the DOM node -->
<div id="root"></div>
</body>
</html>import { createRoot } from 'react-dom/client';
import App from './App.js';
import './styles.css';
const root = createRoot(document.getElementById('root'));
root.render(<App />);import { useState } from 'react';
export default function App() {
return (
<>
<h1>Hello, world!</h1>
<Counter />
</>
);
}
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
You clicked me {count} times
</button>
);
}μ±μ΄ μ¨μ ν Reactλ§μΌλ‘ μμ±λ κ²½μ°, μΆκ°μ μΌλ‘ 루νΈλ₯Ό λ λ§λ€κ±°λ root.renderλ₯Ό λ€μ νΈμΆν νμκ° μμ΅λλ€.
μ΄ μμ λΆν° Reactλ μ 체 μ±μ DOMμ κ΄λ¦¬ν©λλ€. μ»΄ν¬λνΈλ₯Ό λ μΆκ°νλ €λ©΄ App μ»΄ν¬λνΈ μμ μ€μ²©μν€μΈμ. UI μ
λ°μ΄νΈλ κ° μ»΄ν¬λνΈμ Stateλ₯Ό ν΅ν΄ μνν μ μμ΅λλ€. λͺ¨λ¬μ΄λ ν΄νκ³Ό κ°μ μΆκ° μ½ν
μΈ λ₯Ό DOM λ
Έλ μΈλΆμ νμν΄μΌ νλ κ²½μ° Portalλ‘ λ λλ§νμΈμ.
HTMLμ΄ λΉμ΄μμΌλ©΄, μ±μ μλ°μ€ν¬λ¦½νΈ μ½λκ° λ‘λλκ³ μ€νλ λκΉμ§ μ¬μ©μμκ² λΉ νμ΄μ§κ° νμλ©λλ€.
<div id="root"></div>μ΄κ²μ λ§€μ° λλ¦¬κ² λκ»΄μ§ μ μμ΅λλ€! μ΄ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ μλ²μμ λλ λΉλ μ€μ μ»΄ν¬λνΈλ‘λΆν° μ΄κΈ° HTMLμ μμ±ν μ μμ΅λλ€. κ·Έλ¬λ©΄ λ°©λ¬Έμλ μλ°μ€ν¬λ¦½νΈ μ½λκ° λ‘λλκΈ° μ μ ν μ€νΈλ₯Ό μ½κ³ , μ΄λ―Έμ§λ₯Ό λ³΄κ³ , λ§ν¬λ₯Ό ν΄λ¦ν μ μμ΅λλ€. μ΄ μ΅μ νλ₯Ό κΈ°λ³Έμ μΌλ‘ μννλ νλ μμν¬λ₯Ό μ¬μ©νλ κ²μ΄ μ’μ΅λλ€. μ€ν μμ μ λ°λΌ μ΄λ₯Ό μλ² μΈ‘ λ λλ§SSR λλ μ μ μ¬μ΄νΈ μμ±SSG μ΄λΌκ³ ν©λλ€.
μλ² μΈ‘ λ λλ§μ΄λ μ μ μ¬μ΄νΈ μμ±μ μ¬μ©νλ μ±μ createRoot λμ hydrateRootλ₯Ό νΈμΆν΄μΌ ν©λλ€. κ·Έλ¬λ©΄ Reactλ DOM λ
Έλλ₯Ό νκ΄΄νκ³ λ€μ μμ±νλ λμ HTMLμΌλ‘λΆν° Hydrate(μ¬μ¬μ©)ν©λλ€.
Reactλ‘ λΆλΆμ μΌλ‘ μμ±λ νμ΄μ§ λ λλ§νκΈ° {/rendering-a-page-partially-built-with-react/}
νμ΄μ§κ° Reactλ§μΌλ‘ μμ±λμ§ μμ κ²½μ°, Reactκ° κ΄λ¦¬νλ κ° μ΅μμ UIμ λν 루νΈλ₯Ό μμ±νκΈ° μν΄ createRootλ₯Ό μ¬λ¬ λ² νΈμΆν μ μμ΅λλ€. 루νΈλ§λ€ root.renderλ₯Ό νΈμΆν¨μΌλ‘μ¨ κ°κ° λ€λ₯Έ μ½ν
μΈ λ₯Ό νμν μ μμ΅λλ€.
λ€μ μμμμλ μλ‘ λ€λ₯Έ λ κ°μ React μ»΄ν¬λνΈλ₯Ό index.html νμΌμ μ μλ λ κ°μ DOM λ
Έλμ λ λλ§ν©λλ€.
<!DOCTYPE html>
<html>
<head><title>My app</title></head>
<body>
<nav id="navigation"></nav>
<main>
<p>This paragraph is not rendered by React (open index.html to verify).</p>
<section id="comments"></section>
</main>
</body>
</html>import './styles.css';
import { createRoot } from 'react-dom/client';
import { Comments, Navigation } from './Components.js';
const navDomNode = document.getElementById('navigation');
const navRoot = createRoot(navDomNode);
navRoot.render(<Navigation />);
const commentDomNode = document.getElementById('comments');
const commentRoot = createRoot(commentDomNode);
commentRoot.render(<Comments />);export function Navigation() {
return (
<ul>
<NavLink href="/">Home</NavLink>
<NavLink href="/about">About</NavLink>
</ul>
);
}
function NavLink({ href, children }) {
return (
<li>
<a href={href}>{children}</a>
</li>
);
}
export function Comments() {
return (
<>
<h2>Comments</h2>
<Comment text="Hello!" author="Sophie" />
<Comment text="How are you?" author="Sunil" />
</>
);
}
function Comment({ text, author }) {
return (
<p>{text} β <i>{author}</i></p>
);
}nav ul { padding: 0; margin: 0; }
nav ul li { display: inline-block; margin-right: 20px; }document.createElement()λ₯Ό μ¬μ©νμ¬ μ DOM λ
Έλλ₯Ό μμ±νκ³ λ¬Έμμ μλμΌλ‘ μΆκ°ν μλ μμ΅λλ€.
const domNode = document.createElement('div');
const root = createRoot(domNode);
root.render(<Comment />);
document.body.appendChild(domNode); // You can add it anywhere in the documentDOM λ
Έλμμ React νΈλ¦¬λ₯Ό μ κ±°νκ³ μ΄ νΈλ¦¬κ° μ¬μ©νλ λͺ¨λ 리μμ€λ₯Ό μ 리νλ €λ©΄ root.unmountλ₯Ό νΈμΆνμΈμ.
root.unmount();μ΄ κΈ°λ₯μ React μ»΄ν¬λνΈκ° λ€λ₯Έ νλ μμν¬λ‘ μμ±λ μ± λ΄λΆμ μλ κ²½μ°μ μ£Όλ‘ μ μ©ν©λλ€.
κ°μ 루νΈμμ renderλ₯Ό λ λ² μ΄μ νΈμΆν μλ μμ΅λλ€. μ»΄ν¬λνΈ νΈλ¦¬ κ΅¬μ‘°κ° μ΄μ λ λλ§κ³Ό μΌμΉνλ ν, Reactλ κΈ°μ‘΄ Stateλ₯Ό μ μ§ν©λλ€. λ€μ μμμμ μ
λ ₯ μ°½μ μ΄λ»κ² νμ΄ννλ κ΄κ³μμ΄, λ§€ μ΄ λ°λ³΅λλ render νΈμΆλ‘ μΈν μ
λ°μ΄νΈκ° μλ¬΄λ° λ¬Έμ λ₯Ό μΌμΌν€μ§ μμμ μ£Όλͺ©νμΈμ.
import { createRoot } from 'react-dom/client';
import './styles.css';
import App from './App.js';
const root = createRoot(document.getElementById('root'));
let i = 0;
setInterval(() => {
root.render(<App counter={i} />);
i++;
}, 1000);export default function App({counter}) {
return (
<>
<h1>Hello, world! {counter}</h1>
<input placeholder="Type something here" />
</>
);
}renderλ₯Ό μ¬λ¬ λ² νΈμΆνλ κ²½μ°λ ννμ§ μμ΅λλ€. μΌλ°μ μΌλ‘λ, μ»΄ν¬λνΈκ° Stateλ₯Ό μ
λ°μ΄νΈν©λλ€.
By default, React will log all uncaught errors to the console. To implement your own error reporting, you can provide the optional onUncaughtError root option:
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onUncaughtError: (error, errorInfo) => {
console.error(
'Uncaught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);The onUncaughtError option is a function called with two arguments:
- The error that was thrown.
- An errorInfo object that contains the componentStack of the error.
You can use the onUncaughtError root option to display error dialogs:
<!DOCTYPE html>
<html>
<head>
<title>My app</title>
</head>
<body>
<!--
Error dialog in raw HTML
since an error in the React app may crash.
-->
<div id="error-dialog" class="hidden">
<h1 id="error-title" class="text-red"></h1>
<h3>
<pre id="error-message"></pre>
</h3>
<p>
<pre id="error-body"></pre>
</p>
<h4 class="-mb-20">This error occurred at:</h4>
<pre id="error-component-stack" class="nowrap"></pre>
<h4 class="mb-0">Call stack:</h4>
<pre id="error-stack" class="nowrap"></pre>
<div id="error-cause">
<h4 class="mb-0">Caused by:</h4>
<pre id="error-cause-message"></pre>
<pre id="error-cause-stack" class="nowrap"></pre>
</div>
<button
id="error-close"
class="mb-10"
onclick="document.getElementById('error-dialog').classList.add('hidden')"
>
Close
</button>
<h3 id="error-not-dismissible">This error is not dismissible.</h3>
</div>
<!-- This is the DOM node -->
<div id="root"></div>
</body>
</html>label, button { display: block; margin-bottom: 20px; }
html, body { min-height: 300px; }
#error-dialog {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: white;
padding: 15px;
opacity: 0.9;
text-wrap: wrap;
overflow: scroll;
}
.text-red {
color: red;
}
.-mb-20 {
margin-bottom: -20px;
}
.mb-0 {
margin-bottom: 0;
}
.mb-10 {
margin-bottom: 10px;
}
pre {
text-wrap: wrap;
}
pre.nowrap {
text-wrap: nowrap;
}
.hidden {
display: none;
}function reportError({ title, error, componentStack, dismissable }) {
const errorDialog = document.getElementById("error-dialog");
const errorTitle = document.getElementById("error-title");
const errorMessage = document.getElementById("error-message");
const errorBody = document.getElementById("error-body");
const errorComponentStack = document.getElementById("error-component-stack");
const errorStack = document.getElementById("error-stack");
const errorClose = document.getElementById("error-close");
const errorCause = document.getElementById("error-cause");
const errorCauseMessage = document.getElementById("error-cause-message");
const errorCauseStack = document.getElementById("error-cause-stack");
const errorNotDismissible = document.getElementById("error-not-dismissible");
// Set the title
errorTitle.innerText = title;
// Display error message and body
const [heading, body] = error.message.split(/\n(.*)/s);
errorMessage.innerText = heading;
if (body) {
errorBody.innerText = body;
} else {
errorBody.innerText = '';
}
// Display component stack
errorComponentStack.innerText = componentStack;
// Display the call stack
// Since we already displayed the message, strip it, and the first Error: line.
errorStack.innerText = error.stack.replace(error.message, '').split(/\n(.*)/s)[1];
// Display the cause, if available
if (error.cause) {
errorCauseMessage.innerText = error.cause.message;
errorCauseStack.innerText = error.cause.stack;
errorCause.classList.remove('hidden');
} else {
errorCause.classList.add('hidden');
}
// Display the close button, if dismissible
if (dismissable) {
errorNotDismissible.classList.add('hidden');
errorClose.classList.remove("hidden");
} else {
errorNotDismissible.classList.remove('hidden');
errorClose.classList.add("hidden");
}
// Show the dialog
errorDialog.classList.remove("hidden");
}
export function reportCaughtError({error, cause, componentStack}) {
reportError({ title: "Caught Error", error, componentStack, dismissable: true});
}
export function reportUncaughtError({error, cause, componentStack}) {
reportError({ title: "Uncaught Error", error, componentStack, dismissable: false });
}
export function reportRecoverableError({error, cause, componentStack}) {
reportError({ title: "Recoverable Error", error, componentStack, dismissable: true });
}import { createRoot } from "react-dom/client";
import App from "./App.js";
import {reportUncaughtError} from "./reportError";
import "./styles.css";
const container = document.getElementById("root");
const root = createRoot(container, {
onUncaughtError: (error, errorInfo) => {
if (error.message !== 'Known error') {
reportUncaughtError({
error,
componentStack: errorInfo.componentStack
});
}
}
});
root.render(<App />);import { useState } from 'react';
export default function App() {
const [throwError, setThrowError] = useState(false);
if (throwError) {
foo.bar = 'baz';
}
return (
<div>
<span>This error shows the error dialog:</span>
<button onClick={() => setThrowError(true)}>
Throw error
</button>
</div>
);
}By default, React will log all errors caught by an Error Boundary to console.error. To override this behavior, you can provide the optional onCaughtError root option to handle errors caught by an Error Boundary:
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onCaughtError: (error, errorInfo) => {
console.error(
'Caught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);The onCaughtError option is a function called with two arguments:
- The error that was caught by the boundary.
- An errorInfo object that contains the componentStack of the error.
You can use the onCaughtError root option to display error dialogs or filter known errors from logging:
<!DOCTYPE html>
<html>
<head>
<title>My app</title>
</head>
<body>
<!--
Error dialog in raw HTML
since an error in the React app may crash.
-->
<div id="error-dialog" class="hidden">
<h1 id="error-title" class="text-red"></h1>
<h3>
<pre id="error-message"></pre>
</h3>
<p>
<pre id="error-body"></pre>
</p>
<h4 class="-mb-20">This error occurred at:</h4>
<pre id="error-component-stack" class="nowrap"></pre>
<h4 class="mb-0">Call stack:</h4>
<pre id="error-stack" class="nowrap"></pre>
<div id="error-cause">
<h4 class="mb-0">Caused by:</h4>
<pre id="error-cause-message"></pre>
<pre id="error-cause-stack" class="nowrap"></pre>
</div>
<button
id="error-close"
class="mb-10"
onclick="document.getElementById('error-dialog').classList.add('hidden')"
>
Close
</button>
<h3 id="error-not-dismissible">This error is not dismissible.</h3>
</div>
<!-- This is the DOM node -->
<div id="root"></div>
</body>
</html>label, button { display: block; margin-bottom: 20px; }
html, body { min-height: 300px; }
#error-dialog {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: white;
padding: 15px;
opacity: 0.9;
text-wrap: wrap;
overflow: scroll;
}
.text-red {
color: red;
}
.-mb-20 {
margin-bottom: -20px;
}
.mb-0 {
margin-bottom: 0;
}
.mb-10 {
margin-bottom: 10px;
}
pre {
text-wrap: wrap;
}
pre.nowrap {
text-wrap: nowrap;
}
.hidden {
display: none;
}function reportError({ title, error, componentStack, dismissable }) {
const errorDialog = document.getElementById("error-dialog");
const errorTitle = document.getElementById("error-title");
const errorMessage = document.getElementById("error-message");
const errorBody = document.getElementById("error-body");
const errorComponentStack = document.getElementById("error-component-stack");
const errorStack = document.getElementById("error-stack");
const errorClose = document.getElementById("error-close");
const errorCause = document.getElementById("error-cause");
const errorCauseMessage = document.getElementById("error-cause-message");
const errorCauseStack = document.getElementById("error-cause-stack");
const errorNotDismissible = document.getElementById("error-not-dismissible");
// Set the title
errorTitle.innerText = title;
// Display error message and body
const [heading, body] = error.message.split(/\n(.*)/s);
errorMessage.innerText = heading;
if (body) {
errorBody.innerText = body;
} else {
errorBody.innerText = '';
}
// Display component stack
errorComponentStack.innerText = componentStack;
// Display the call stack
// Since we already displayed the message, strip it, and the first Error: line.
errorStack.innerText = error.stack.replace(error.message, '').split(/\n(.*)/s)[1];
// Display the cause, if available
if (error.cause) {
errorCauseMessage.innerText = error.cause.message;
errorCauseStack.innerText = error.cause.stack;
errorCause.classList.remove('hidden');
} else {
errorCause.classList.add('hidden');
}
// Display the close button, if dismissible
if (dismissable) {
errorNotDismissible.classList.add('hidden');
errorClose.classList.remove("hidden");
} else {
errorNotDismissible.classList.remove('hidden');
errorClose.classList.add("hidden");
}
// Show the dialog
errorDialog.classList.remove("hidden");
}
export function reportCaughtError({error, cause, componentStack}) {
reportError({ title: "Caught Error", error, componentStack, dismissable: true});
}
export function reportUncaughtError({error, cause, componentStack}) {
reportError({ title: "Uncaught Error", error, componentStack, dismissable: false });
}
export function reportRecoverableError({error, cause, componentStack}) {
reportError({ title: "Recoverable Error", error, componentStack, dismissable: true });
}import { createRoot } from "react-dom/client";
import App from "./App.js";
import {reportCaughtError} from "./reportError";
import "./styles.css";
const container = document.getElementById("root");
const root = createRoot(container, {
onCaughtError: (error, errorInfo) => {
if (error.message !== 'Known error') {
reportCaughtError({
error,
componentStack: errorInfo.componentStack,
});
}
}
});
root.render(<App />);import { useState } from 'react';
import { ErrorBoundary } from "react-error-boundary";
export default function App() {
const [error, setError] = useState(null);
function handleUnknown() {
setError("unknown");
}
function handleKnown() {
setError("known");
}
return (
<>
<ErrorBoundary
fallbackRender={fallbackRender}
onReset={(details) => {
setError(null);
}}
>
{error != null && <Throw error={error} />}
<span>This error will not show the error dialog:</span>
<button onClick={handleKnown}>
Throw known error
</button>
<span>This error will show the error dialog:</span>
<button onClick={handleUnknown}>
Throw unknown error
</button>
</ErrorBoundary>
</>
);
}
function fallbackRender({ resetErrorBoundary }) {
return (
<div role="alert">
<h3>Error Boundary</h3>
<p>Something went wrong.</p>
<button onClick={resetErrorBoundary}>Reset</button>
</div>
);
}
function Throw({error}) {
if (error === "known") {
throw new Error('Known error')
} else {
foo.bar = 'baz';
}
}{
"dependencies": {
"react": "19.0.0-rc-3edc000d-20240926",
"react-dom": "19.0.0-rc-3edc000d-20240926",
"react-scripts": "^5.0.0",
"react-error-boundary": "4.0.3"
},
"main": "/index.js"
}React may automatically render a component a second time to attempt to recover from an error thrown in render. If successful, React will log a recoverable error to the console to notify the developer. To override this behavior, you can provide the optional onRecoverableError root option:
import { createRoot } from 'react-dom/client';
const root = createRoot(
document.getElementById('root'),
{
onRecoverableError: (error, errorInfo) => {
console.error(
'Recoverable error',
error,
error.cause,
errorInfo.componentStack,
);
}
}
);
root.render(<App />);The onRecoverableError option is a function called with two arguments:
- The error that React throws. Some errors may include the original cause as error.cause.
- An errorInfo object that contains the componentStack of the error.
You can use the onRecoverableError root option to display error dialogs:
<!DOCTYPE html>
<html>
<head>
<title>My app</title>
</head>
<body>
<!--
Error dialog in raw HTML
since an error in the React app may crash.
-->
<div id="error-dialog" class="hidden">
<h1 id="error-title" class="text-red"></h1>
<h3>
<pre id="error-message"></pre>
</h3>
<p>
<pre id="error-body"></pre>
</p>
<h4 class="-mb-20">This error occurred at:</h4>
<pre id="error-component-stack" class="nowrap"></pre>
<h4 class="mb-0">Call stack:</h4>
<pre id="error-stack" class="nowrap"></pre>
<div id="error-cause">
<h4 class="mb-0">Caused by:</h4>
<pre id="error-cause-message"></pre>
<pre id="error-cause-stack" class="nowrap"></pre>
</div>
<button
id="error-close"
class="mb-10"
onclick="document.getElementById('error-dialog').classList.add('hidden')"
>
Close
</button>
<h3 id="error-not-dismissible">This error is not dismissible.</h3>
</div>
<!-- This is the DOM node -->
<div id="root"></div>
</body>
</html>label, button { display: block; margin-bottom: 20px; }
html, body { min-height: 300px; }
#error-dialog {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: white;
padding: 15px;
opacity: 0.9;
text-wrap: wrap;
overflow: scroll;
}
.text-red {
color: red;
}
.-mb-20 {
margin-bottom: -20px;
}
.mb-0 {
margin-bottom: 0;
}
.mb-10 {
margin-bottom: 10px;
}
pre {
text-wrap: wrap;
}
pre.nowrap {
text-wrap: nowrap;
}
.hidden {
display: none;
}function reportError({ title, error, componentStack, dismissable }) {
const errorDialog = document.getElementById("error-dialog");
const errorTitle = document.getElementById("error-title");
const errorMessage = document.getElementById("error-message");
const errorBody = document.getElementById("error-body");
const errorComponentStack = document.getElementById("error-component-stack");
const errorStack = document.getElementById("error-stack");
const errorClose = document.getElementById("error-close");
const errorCause = document.getElementById("error-cause");
const errorCauseMessage = document.getElementById("error-cause-message");
const errorCauseStack = document.getElementById("error-cause-stack");
const errorNotDismissible = document.getElementById("error-not-dismissible");
// Set the title
errorTitle.innerText = title;
// Display error message and body
const [heading, body] = error.message.split(/\n(.*)/s);
errorMessage.innerText = heading;
if (body) {
errorBody.innerText = body;
} else {
errorBody.innerText = '';
}
// Display component stack
errorComponentStack.innerText = componentStack;
// Display the call stack
// Since we already displayed the message, strip it, and the first Error: line.
errorStack.innerText = error.stack.replace(error.message, '').split(/\n(.*)/s)[1];
// Display the cause, if available
if (error.cause) {
errorCauseMessage.innerText = error.cause.message;
errorCauseStack.innerText = error.cause.stack;
errorCause.classList.remove('hidden');
} else {
errorCause.classList.add('hidden');
}
// Display the close button, if dismissible
if (dismissable) {
errorNotDismissible.classList.add('hidden');
errorClose.classList.remove("hidden");
} else {
errorNotDismissible.classList.remove('hidden');
errorClose.classList.add("hidden");
}
// Show the dialog
errorDialog.classList.remove("hidden");
}
export function reportCaughtError({error, cause, componentStack}) {
reportError({ title: "Caught Error", error, componentStack, dismissable: true});
}
export function reportUncaughtError({error, cause, componentStack}) {
reportError({ title: "Uncaught Error", error, componentStack, dismissable: false });
}
export function reportRecoverableError({error, cause, componentStack}) {
reportError({ title: "Recoverable Error", error, componentStack, dismissable: true });
}import { createRoot } from "react-dom/client";
import App from "./App.js";
import {reportRecoverableError} from "./reportError";
import "./styles.css";
const container = document.getElementById("root");
const root = createRoot(container, {
onRecoverableError: (error, errorInfo) => {
reportRecoverableError({
error,
cause: error.cause,
componentStack: errorInfo.componentStack,
});
}
});
root.render(<App />);import { useState } from 'react';
import { ErrorBoundary } from "react-error-boundary";
// π© Bug: Never do this. This will force an error.
let errorThrown = false;
export default function App() {
return (
<>
<ErrorBoundary
fallbackRender={fallbackRender}
>
{!errorThrown && <Throw />}
<p>This component threw an error, but recovered during a second render.</p>
<p>Since it recovered, no Error Boundary was shown, but <code>onRecoverableError</code> was used to show an error dialog.</p>
</ErrorBoundary>
</>
);
}
function fallbackRender() {
return (
<div role="alert">
<h3>Error Boundary</h3>
<p>Something went wrong.</p>
</div>
);
}
function Throw({error}) {
// Simulate an external value changing during concurrent render.
errorThrown = true;
foo.bar = 'baz';
}{
"dependencies": {
"react": "19.0.0-rc-3edc000d-20240926",
"react-dom": "19.0.0-rc-3edc000d-20240926",
"react-scripts": "^5.0.0",
"react-error-boundary": "4.0.3"
},
"main": "/index.js"
}루νΈλ₯Ό μμ±νλλ° μ무κ²λ νμλμ§ μμ΅λλ€ {/ive-created-a-root-but-nothing-is-displayed/}
μ€μ λ‘ μ±μ 루νΈμ λ λλ§νλ κ²μ μμ§ μμλμ§ νμΈνμΈμ.
import { createRoot } from 'react-dom/client';
import App from './App.js';
const root = createRoot(document.getElementById('root'));
root.render(<App />);root.render(...) λͺ
λ Ή μμ΄λ μ무κ²λ νμλμ§ μμ΅λλ€.
I'm getting an error: "You passed a second argument to root.render" {/im-getting-an-error-you-passed-a-second-argument-to-root-render/}
A common mistake is to pass the options for createRoot to root.render(...):
Warning: You passed a second argument to root.render(...) but it only accepts one argument.
To fix, pass the root options to createRoot(...), not root.render(...):
// π© Wrong: root.render only takes one argument.
root.render(App, {onUncaughtError});
// β
Correct: pass options to createRoot.
const root = createRoot(container, {onUncaughtError});
root.render(<App />);"λμ 컨ν μ΄λκ° DOM μ리먼νΈκ° μλλλ€" λΌλ μ€λ₯κ° λ°μν©λλ€. {/im-getting-an-error-target-container-is-not-a-dom-element/}
This error means that whatever you're passing to createRoot is not a DOM node.
If you're not sure what's happening, try logging it:
const domNode = document.getElementById('root');
console.log(domNode); // ???
const root = createRoot(domNode);
root.render(<App />);μλ₯Ό λ€μ΄ domNodeκ° nullμ΄λ©΄ getElementById κ° nullμ λ°ννμμ μλ―Έν©λλ€. μ΄λ νΈμΆ μμ μ λ¬Έμμ μ§μ λ IDλ₯Ό κ°μ§ λ
Έλκ° μλ κ²½μ°μ λ°μν©λλ€. μ¬κΈ°μλ λͺ κ°μ§ μ΄μ κ° μμ μ μμ΅λλ€.
- μ°Ύκ³ μ νλ IDκ° HTML νμΌμμ μ¬μ©ν IDμ λ€λ₯Ό μ μμ΅λλ€. μ€νκ° μλμ§ νμΈνμΈμ!
- λ²λ€μ
<script>νκ·Έλ HTMLμμ κ·Έλ³΄λ€ λ€μ μλ DOM λ Έλλ₯Ό "μΈμν " μ μμ΅λλ€.
λλ€λ₯Έ μΌλ°μ μΈ μ¬λ‘λ createRoot(domNode) λμ createRoot(<App />)μΌλ‘ μμ±νμ κ²½μ°μ
λλ€.
"ν¨μκ° React μμμΌλ‘ μ ν¨νμ§ μμ΅λλ€" μ€λ₯κ° λ°μν©λλ€. {/im-getting-an-error-functions-are-not-valid-as-a-react-child/}
μ΄ μ€λ₯λ root.renderμ μ λ¬νλ κ²μ΄ React μ»΄ν¬λνΈκ° μλμ μλ―Έν©λλ€.
μ΄ μ€λ₯λ <Component /> λμ Componentλ‘ root.renderλ₯Ό νΈμΆν λ λ°μν μ μμ΅λλ€.
// π© Wrong: App is a function, not a Component.
root.render(App);
// β
Correct: <App /> is a component.
root.render(<App />);λλ ν¨μλ₯Ό νΈμΆν κ²°κ³Ό λμ root.renderμ ν¨μ μ체λ₯Ό μ λ¬νμ λλ λ°μν μ μμ΅λλ€.
// π© Wrong: createApp is a function, not a component.
root.render(createApp);
// β
Correct: call createApp to return a component.
root.render(createApp());μλ²μμ λ λλ§λ HTMLμ΄ μ²μλΆν° λ€μ μμ±λ©λλ€ {/my-server-rendered-html-gets-re-created-from-scratch/}
μ±μ΄ μλ²μμ λ λλ§λκ³ Reactμ μ΄κΈ° HTMLμ ν¬ν¨νλ κ²½μ°μ, 루νΈλ₯Ό μμ±ν΄μ root.renderλ₯Ό νΈμΆνλ©΄, λͺ¨λ HTMLμ΄ μμ λκ³ λͺ¨λ DOM λ
Έλκ° μ²μλΆν° λ€μ μμ±λλ κ²μ λ³Ό μ μμ΅λλ€. μ΄λ κ² νλ©΄ μλκ° λλ €μ§κ³ , ν¬μ»€μ€μ μ€ν¬λ‘€ μμΉκ° μ¬μ€μ λλ©°, κ·Έ λ°μ λ€λ₯Έ μ¬μ©μ μ
λ ₯λ€μ΄ μμ€λ μ μμ΅λλ€.
μλ²μμ λ λλ§λ μ±μ createRoot λμ hydrateRootλ₯Ό μ¬μ©ν΄μΌ ν©λλ€.
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(
document.getElementById('root'),
<App />
);APIκ° λ€λ₯΄λ€λ μ μ μ μνμΈμ. νΉν, μΌλ°μ μΌλ‘λ root.renderλ₯Ό μμ νΈμΆνμ§ μμ΅λλ€.