@@ -1140,6 +1191,14 @@ function Throw({error}) {
}
```
+=======
+ Purposefully using HTML content that differs from the server-rendered content to trigger recoverable errors.
+-->
+
Server content before hydration.
+
+
+```
+>>>>>>> 49284218b1f5c94f930f8a9b305040dbe7d3dd48
## 문제 해결 {/*troubleshooting*/}
diff --git a/src/content/reference/react-dom/createPortal.md b/src/content/reference/react-dom/createPortal.md
index 2fda3662e..8315a8be4 100644
--- a/src/content/reference/react-dom/createPortal.md
+++ b/src/content/reference/react-dom/createPortal.md
@@ -251,7 +251,7 @@ Portal은 React 루트가 React로 빌드되지 않은 정적 또는 서버 렌
-```html index.html
+```html public/index.html
My app
diff --git a/src/content/reference/react-dom/static/prerender.md b/src/content/reference/react-dom/static/prerender.md
index bb726683f..07e3ebddc 100644
--- a/src/content/reference/react-dom/static/prerender.md
+++ b/src/content/reference/react-dom/static/prerender.md
@@ -229,8 +229,13 @@ async function renderToString() {
const {prelude} = await prerender(, {
bootstrapScripts: ['/main.js']
});
+<<<<<<< HEAD
const reader = stream.getReader();
+=======
+
+ const reader = prelude.getReader();
+>>>>>>> 49284218b1f5c94f930f8a9b305040dbe7d3dd48
let content = '';
while (true) {
const {done, value} = await reader.read();
@@ -291,6 +296,10 @@ Suspense-enabled data fetching without the use of an opinionated framework is no
### My stream doesn't start until the entire app is rendered {/*my-stream-doesnt-start-until-the-entire-app-is-rendered*/}
+<<<<<<< HEAD
The `prerender` response waits for the entire app to finish rendering, including waiting for all suspense boundaries to resolve, before resolving. It is designed for static site generation (SSG) ahead of time and does not support streaming more content as it loads.
+=======
+The `prerender` response waits for the entire app to finish rendering, including waiting for all Suspense boundaries to resolve, before resolving. It is designed for static site generation (SSG) ahead of time and does not support streaming more content as it loads.
+>>>>>>> 49284218b1f5c94f930f8a9b305040dbe7d3dd48
To stream content as it loads, use a streaming server render API like [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream).
diff --git a/src/content/reference/react-dom/static/prerenderToNodeStream.md b/src/content/reference/react-dom/static/prerenderToNodeStream.md
index a90ca86b3..278e9817a 100644
--- a/src/content/reference/react-dom/static/prerenderToNodeStream.md
+++ b/src/content/reference/react-dom/static/prerenderToNodeStream.md
@@ -289,7 +289,7 @@ Suspense-enabled data fetching without the use of an opinionated framework is no
### My stream doesn't start until the entire app is rendered {/*my-stream-doesnt-start-until-the-entire-app-is-rendered*/}
-The `prerenderToNodeStream` response waits for the entire app to finish rendering, including waiting for all suspense boundaries to resolve, before resolving. It is designed for static site generation (SSG) ahead of time and does not support streaming more content as it loads.
+The `prerenderToNodeStream` response waits for the entire app to finish rendering, including waiting for all Suspense boundaries to resolve, before resolving. It is designed for static site generation (SSG) ahead of time and does not support streaming more content as it loads.
To stream content as it loads, use a streaming server render API like [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream).
diff --git a/src/content/reference/react/Component.md b/src/content/reference/react/Component.md
index 8bb096417..a23eb46c7 100644
--- a/src/content/reference/react/Component.md
+++ b/src/content/reference/react/Component.md
@@ -1273,7 +1273,11 @@ button { margin-left: 10px; }
error boundary 컴포넌트를 구현하려면 오류에 대한 응답으로 state를 업데이트하고 사용자에게 오류 메시지를 표시할 수 있는 [`static getDerivedStateFromError`](#static-getderivedstatefromerror)를 제공해야 합니다. 또한 선택적으로 [`componentDidCatch`](#componentdidcatch)를 구현하여 분석 서비스에 오류를 기록하는 등의 추가 로직을 추가할 수도 있습니다.
-```js {7-10,12-19}
+ With [`captureOwnerStack`](/reference/react/captureOwnerStack) you can include the Owner Stack during development.
+
+```js {9-12,14-27}
+import * as React from 'react';
+
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
@@ -1286,12 +1290,18 @@ class ErrorBoundary extends React.Component {
}
componentDidCatch(error, info) {
- // Example "componentStack":
- // in ComponentThatThrows (created by App)
- // in ErrorBoundary (created by App)
- // in div (created by App)
- // in App
- logErrorToMyService(error, info.componentStack);
+ logErrorToMyService(
+ error,
+ // Example "componentStack":
+ // in ComponentThatThrows (created by App)
+ // in ErrorBoundary (created by App)
+ // in div (created by App)
+ // in App
+ info.componentStack,
+ // Only available in react@canary.
+ // Warning: Owner Stack is not available in production.
+ React.captureOwnerStack(),
+ );
}
render() {
diff --git a/src/content/reference/react/captureOwnerStack.md b/src/content/reference/react/captureOwnerStack.md
new file mode 100644
index 000000000..f8ed21a8c
--- /dev/null
+++ b/src/content/reference/react/captureOwnerStack.md
@@ -0,0 +1,452 @@
+---
+title: captureOwnerStack
+---
+
+
+
+The `captureOwnerStack` API is currently only available in React's Canary and experimental channels. Learn more about [React's release channels here](/community/versioning-policy#all-release-channels).
+
+
+
+
+
+`captureOwnerStack` reads the current Owner Stack in development and returns it as a string if available.
+
+```js
+const stack = captureOwnerStack();
+```
+
+
+
+
+
+---
+
+## Reference {/*reference*/}
+
+### `captureOwnerStack()` {/*captureownerstack*/}
+
+Call `captureOwnerStack` to get the current Owner Stack.
+
+```js {5,5}
+import * as React from 'react';
+
+function Component() {
+ if (process.env.NODE_ENV !== 'production') {
+ const ownerStack = React.captureOwnerStack();
+ console.log(ownerStack);
+ }
+}
+```
+
+#### Parameters {/*parameters*/}
+
+`captureOwnerStack` does not take any parameters.
+
+#### Returns {/*returns*/}
+
+`captureOwnerStack` returns `string | null`.
+
+Owner Stacks are available in
+- Component render
+- Effects (e.g. `useEffect`)
+- React's event handlers (e.g. ``)
+- React error handlers ([React Root options](/reference/react-dom/client/createRoot#parameters) `onCaughtError`, `onRecoverableError`, and `onUncaughtError`)
+
+If no Owner Stack is available, `null` is returned (see [Troubleshooting: The Owner Stack is `null`](#the-owner-stack-is-null)).
+
+#### Caveats {/*caveats*/}
+
+- Owner Stacks are only available in development. `captureOwnerStack` will always return `null` outside of development.
+
+
+
+#### Owner Stack vs Component Stack {/*owner-stack-vs-component-stack*/}
+
+The Owner Stack is different from the Component Stack available in React error handlers like [`errorInfo.componentStack` in `onUncaughtError`](/reference/react-dom/client/hydrateRoot#show-a-dialog-for-uncaught-errors).
+
+For example, consider the following code:
+
+
+
+```js src/App.js
+import {Suspense} from 'react';
+
+function SubComponent({disabled}) {
+ if (disabled) {
+ throw new Error('disabled');
+ }
+}
+
+export function Component({label}) {
+ return (
+
+ );
+}
+
+function Navigation() {
+ return null;
+}
+
+export default function App({children}) {
+ return (
+
+
+
+ {children}
+
+
+ );
+}
+```
+
+```js src/index.js
+import {captureOwnerStack} from 'react';
+import {createRoot} from 'react-dom/client';
+import App, {Component} from './App.js';
+import './styles.css';
+
+createRoot(document.createElement('div'), {
+ onUncaughtError: (error, errorInfo) => {
+ // The stacks are logged instead of showing them in the UI directly to
+ // highlight that browsers will apply sourcemaps to the logged stacks.
+ // Note that sourcemapping is only applied in the real browser console not
+ // in the fake one displayed on this page.
+ // Press "fork" to be able to view the sourcemapped stack in a real console.
+ console.log(errorInfo.componentStack);
+ console.log(captureOwnerStack());
+ },
+}).render(
+
+
+
+);
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "canary",
+ "react-dom": "canary",
+ "react-scripts": "latest"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
+```
+
+```html public/index.html hidden
+
+
+
+
+
+ Document
+
+
+ Check the console output.
+
+
+```
+
+
+
+`SubComponent` would throw an error.
+The Component Stack of that error would be
+
+```
+at SubComponent
+at fieldset
+at Component
+at main
+at React.Suspense
+at App
+```
+
+However, the Owner Stack would only read
+
+```
+at Component
+```
+
+Neither `App` nor the DOM components (e.g. `fieldset`) are considered Owners in this Stack since they didn't contribute to "creating" the node containing `SubComponent`. `App` and DOM components only forwarded the node. `App` just rendered the `children` node as opposed to `Component` which created a node containing `SubComponent` via ``.
+
+Neither `Navigation` nor `legend` are in the stack at all since it's only a sibling to a node containing ``.
+
+`SubComponent` is omitted because it's already part of the callstack.
+
+
+
+## Usage {/*usage*/}
+
+### Enhance a custom error overlay {/*enhance-a-custom-error-overlay*/}
+
+```js [[1, 5, "console.error"], [4, 7, "captureOwnerStack"]]
+import { captureOwnerStack } from "react";
+import { instrumentedConsoleError } from "./errorOverlay";
+
+const originalConsoleError = console.error;
+console.error = function patchedConsoleError(...args) {
+ originalConsoleError.apply(console, args);
+ const ownerStack = captureOwnerStack();
+ onConsoleError({
+ // Keep in mind that in a real application, console.error can be
+ // called with multiple arguments which you should account for.
+ consoleMessage: args[0],
+ ownerStack,
+ });
+};
+```
+
+If you intercept `console.error` calls to highlight them in an error overlay, you can call `captureOwnerStack` to include the Owner Stack.
+
+
+
+```css src/styles.css
+* {
+ box-sizing: border-box;
+}
+
+body {
+ font-family: sans-serif;
+ margin: 20px;
+ padding: 0;
+}
+
+h1 {
+ margin-top: 0;
+ font-size: 22px;
+}
+
+h2 {
+ margin-top: 0;
+ font-size: 20px;
+}
+
+code {
+ font-size: 1.2em;
+}
+
+ul {
+ padding-inline-start: 20px;
+}
+
+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;
+}
+```
+
+```html public/index.html hidden
+
+
+
+ My app
+
+
+
+
+
Error
+
+
+
+
Owner Stack:
+
+
+
+
+
+
+
+
+```
+
+```js src/errorOverlay.js
+
+export function onConsoleError({ consoleMessage, ownerStack }) {
+ const errorDialog = document.getElementById("error-dialog");
+ const errorBody = document.getElementById("error-body");
+ const errorOwnerStack = document.getElementById("error-owner-stack");
+
+ // Display console.error() message
+ errorBody.innerText = consoleMessage;
+
+ // Display owner stack
+ errorOwnerStack.innerText = ownerStack;
+
+ // Show the dialog
+ errorDialog.classList.remove("hidden");
+}
+```
+
+```js src/index.js active
+import { captureOwnerStack } from "react";
+import { createRoot } from "react-dom/client";
+import App from './App';
+import { onConsoleError } from "./errorOverlay";
+import './styles.css';
+
+const originalConsoleError = console.error;
+console.error = function patchedConsoleError(...args) {
+ originalConsoleError.apply(console, args);
+ const ownerStack = captureOwnerStack();
+ onConsoleError({
+ // Keep in mind that in a real application, console.error can be
+ // called with multiple arguments which you should account for.
+ consoleMessage: args[0],
+ ownerStack,
+ });
+};
+
+const container = document.getElementById("root");
+createRoot(container).render();
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "canary",
+ "react-dom": "canary",
+ "react-scripts": "latest"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
+```
+
+```js src/App.js
+function Component() {
+ return ;
+}
+
+export default function App() {
+ return ;
+}
+```
+
+
+
+## Troubleshooting {/*troubleshooting*/}
+
+### The Owner Stack is `null` {/*the-owner-stack-is-null*/}
+
+The call of `captureOwnerStack` happened outside of a React controlled function e.g. in a `setTimeout` callback, after a `fetch` call or in a custom DOM event handler. During render, Effects, React event handlers, and React error handlers (e.g. `hydrateRoot#options.onCaughtError`) Owner Stacks should be available.
+
+In the example below, clicking the button will log an empty Owner Stack because `captureOwnerStack` was called during a custom DOM event handler. The Owner Stack must be captured earlier e.g. by moving the call of `captureOwnerStack` into the Effect body.
+
+
+```js
+import {captureOwnerStack, useEffect} from 'react';
+
+export default function App() {
+ useEffect(() => {
+ // Should call `captureOwnerStack` here.
+ function handleEvent() {
+ // Calling it in a custom DOM event handler is too late.
+ // The Owner Stack will be `null` at this point.
+ console.log('Owner Stack: ', captureOwnerStack());
+ }
+
+ document.addEventListener('click', handleEvent);
+
+ return () => {
+ document.removeEventListener('click', handleEvent);
+ }
+ })
+
+ return ;
+}
+```
+
+```json package.json hidden
+{
+ "dependencies": {
+ "react": "canary",
+ "react-dom": "canary",
+ "react-scripts": "latest"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ }
+}
+```
+
+
+
+### `captureOwnerStack` is not available {/*captureownerstack-is-not-available*/}
+
+`captureOwnerStack` is only exported in development builds. It will be `undefined` in production builds. If `captureOwnerStack` is used in files that are bundled for production and development, you should conditionally access it from a namespace import.
+
+```js
+// Don't use named imports of `captureOwnerStack` in files that are bundled for development and production.
+import {captureOwnerStack} from 'react';
+// Use a namespace import instead and access `captureOwnerStack` conditionally.
+import * as React from 'react';
+
+if (process.env.NODE_ENV !== 'production') {
+ const ownerStack = React.captureOwnerStack();
+ console.log('Owner Stack', ownerStack);
+}
+```
diff --git a/src/content/reference/react/useActionState.md b/src/content/reference/react/useActionState.md
index acd44c8a5..8a7f3b7a5 100644
--- a/src/content/reference/react/useActionState.md
+++ b/src/content/reference/react/useActionState.md
@@ -67,9 +67,15 @@ function StatefulForm({}) {
`useActionState`는 다음 세 가지 값을 담은 배열을 반환합니다.
+<<<<<<< HEAD
1. 현재 State입니다. 첫 렌더링 시에는 `initialState`와 일치하며, 액션이 실행된 후에는 액션이 반환한 값과 일치합니다.
2. `