resume은 Readable Web Stream을 이용해 사전 렌더링된 React 트리를 스트리밍합니다.
const stream = await resume(reactNode, postponedState, options?)레퍼런스
resume(node, postponedState, options?)
resume을 호출해 사전 렌더링된 React 트리의 렌더링을 재개하고, 이를 HTML로 Readable Web Stream에 렌더링합니다.
import { resume } from 'react-dom/server';
import {getPostponedState} from './storage';
async function handler(request, writable) {
const postponed = await getPostponedState(request);
const resumeStream = await resume(<App />, postponed);
return resumeStream.pipeTo(writable)
}매개변수
reactNode:prerender를 호출할 때 전달한 React 노드입니다. 예를 들어,<App />과 같은 JSX 엘리먼트입니다. 전체 문서를 나타낼 것으로 예상되므로App컴포넌트는<html>태그를 렌더링해야 합니다.postponedState: prerender API에서 반환된 불분명한postpone객체로, 저장해 둔 위치(예: Redis, 파일, S3)에서 불러옵니다.- optional
options: 스트리밍 옵션을 지정할 수 있는 객체입니다.- optional
nonce:script-srcContent-Security-Policy에서 스크립트를 허용하기 위한nonce문자열입니다. - optional
signal: 서버 렌더링을 중단하고 나머지를 클라이언트에서 렌더링할 수 있게 하는 중단 신호입니다. - optional
onError: 서버 오류가 발생할 때마다, 복구 가능 또는 불가능에 관계없이 호출되는 콜백입니다. 기본적으로console.error만 호출합니다. 충돌 보고를 기록하도록 재정의하는 경우에도 반드시console.error를 호출해야 합니다.
- optional
반환값
resume은 Promise를 반환합니다.
resume이 shell을 성공적으로 생성하면, 해당 Promise는 Writable Web Stream으로 파이프할 수 있는 Readable Web Stream으로 이행됩니다.- shell에서 오류가 발생하면, 해당 Promise는 그 오류와 함께 거부됩니다.
반환된 스트림은 다음과 같은 추가적인 프로퍼티를 가지고 있습니다.
allReady: 모든 렌더링이 완료되면 이행되는 Promise입니다. 크롤러와 정적 생성을 위해 응답을 반환하기 전에await stream.allReady를 사용할 수 있습니다. 이렇게 하면 점진적 로딩은 사용할 수 없습니다. 스트림에는 최종 HTML이 포함됩니다.
주의 사항
resume은bootstrapScripts,bootstrapScriptContent,bootstrapModules옵션을 받지 않습니다. 대신postponedState를 생성하는prerender호출에 이 옵션들을 전달해야 합니다. 또한 쓰기 가능한 스트림에 부트스트랩 콘텐츠를 수동으로 주입할 수도 있습니다.prerender와resume에서 접두사가 동일해야 하므로,resume은identifierPrefix를 받지 않습니다.nonce는 prerender에 전달할 수 없으므로, prerender에 스크립트를 제공하지 않는 경우에만resume에nonce를 전달해야 합니다.resume은 사전 렌더링이 완전히 완료되지 않은 컴포넌트를 찾을 때까지 루트부터 다시 렌더링합니다. 사전 렌더링이 완전히 완료된 컴포넌트(해당 컴포넌트와 자식들의 사전 렌더링이 모두 완료된 경우)만 완전히 건너뜁니다.
사용법
사전 렌더링 재개하기
import { flushReadableStreamToFrame, getUser, Postponed, sleep, } from "./demo-helpers"; import { StrictMode, Suspense, use, useEffect } from "react"; import { prerender } from "react-dom/static"; import { resume } from "react-dom/server"; import { hydrateRoot } from "react-dom/client"; function Header() { return <header>Me and my descendants can be prerendered</header>; } const { promise: cookies, resolve: resolveCookies } = Promise.withResolvers(); function Main() { const { sessionID } = use(cookies); const user = getUser(sessionID); useEffect(() => { console.log("reached interactivity!"); }, []); return ( <main> Hello, {user.name}! <button onClick={() => console.log("hydrated!")}> Clicking me requires hydration. </button> </main> ); } function Shell({ children }) { // In a real app, this is where you would put your html and body. // We're just using tags here we can include in an existing body for demonstration purposes return ( <html> <body>{children}</body> </html> ); } function App() { return ( <Shell> <Suspense fallback="loading header"> <Header /> </Suspense> <Suspense fallback="loading main"> <Main /> </Suspense> </Shell> ); } async function main(frame) { // Layer 1 const controller = new AbortController(); const prerenderedApp = prerender(<App />, { signal: controller.signal, onError(error) { if (error instanceof Postponed) { } else { console.error(error); } }, }); // We're immediately aborting in a macrotask. // Any data fetching that's not available synchronously, or in a microtask, will not have finished. setTimeout(() => { controller.abort(new Postponed()); }); const { prelude, postponed } = await prerenderedApp; await flushReadableStreamToFrame(prelude, frame); // Layer 2 // Just waiting here for demonstration purposes. // In a real app, the prelude and postponed state would've been serialized in Layer 1 and Layer would deserialize them. // The prelude content could be flushed immediated as plain HTML while // React is continuing to render from where the prerender left off. await sleep(2000); // You would get the cookies from the incoming HTTP request resolveCookies({ sessionID: "abc" }); const stream = await resume(<App />, postponed); await flushReadableStreamToFrame(stream, frame); // Layer 3 // Just waiting here for demonstration purposes. await sleep(2000); hydrateRoot(frame.contentWindow.document, <App />); } main(document.getElementById("container"));
추가로 읽어보기
재개 동작은 renderToReadableStream과 유사합니다. 더 많은 예시는 renderToReadableStream의 사용법 섹션을 확인하세요.
prerender의 사용법 섹션에는 prerender 사용 방법에 대한 예시가 포함되어 있습니다.