CS/프레임워크&라이브러리

React v18 - 2

H.E 2024. 2. 24. 19:49

[ React DOM Client ]

react-dom/client에 새로운 API가 추가되었음

▶ createRoot(domNode, options?)

createRoot는 브라우저 DOM 노드 안에 React 컴포넌트를 표시하는 루트를 생성할 수 있음

'ReactDOM.render' 대신 사용하며, 리액트 18의 새로운 기능은 이것없이 동작하지 않음

 

import { createRoot } from 'react-dom/client';

const domNode = document.getElementById('root');
const root = createRoot(domNode);
root.render(<App />);

리액트는 domNode를 위해서 root를 만들것이고, 그 안에 있는 DOM을 관리

root를 만든 후에는, root.render 메서드를 호출해서 그 안에 리액트 컴포넌트를 보여지도록 하면 됨

리액트로 빌드된 어플리케이션은 대개로 root 컴포넌트를 위한 하나의 createRoot을 가지고 있음

 

온전히 React만으로 빌드된 앱에서는 일반적으로 루트 컴포넌트에 대한 createRoot 호출이 하나만 있지만

페이지의 일부에 React를 “뿌려서”(sprinkles) 사용하는 페이지의 경우에는 루트를 필요한 만큼 많이 작성할 수도 있음

 

매개변수

  • domNode
    • DOM 요소
    • 리액트는 이 DOM 요소를 위한 root를 만들것이고 너가 렌더된 리액트 컨텐츠를 보여지도록 하기 위해서 render 메서드 같은 함수를 사용할수 있도록 함
  • options
    • 리액트 root를 위한 옵션 객체
    • optional onRecoverableError : 리액트가 에러로부터 자동으로 복구할때 호출되는 콜백함수
    • optional identifierPrefix : 리액트가 useId에 의해 생성된 ID에 사용하는 접두사
      같은 페이지에 여러개의 roots들이 존재할때 충돌을 피하기 위해서 유용함

주의사항

  • 만약에 당신의 앱이 서버에서 렌더링 된다면, createRoot은 지원되지 않음 대신에 hydrateRoot을 사용
  • 앱 안에서는 하나의 createRoot만 가질 가능성이 있음. 만약 프레임워크를 사용한다면 이것을 호출하도록 해야함
  • 만약 컴포넌트의 자식이 아닌 DOM tree의 다른 부분이 있는 JSX를 렌더하고 싶다면, (예를 들어서 모달창이나 tooltip 같은) createRoot 보다는 createPortal을 사용

 

이전 버전과 현재 버전 차이

 before

import ReactDOM from 'react-dom'
import App from 'App'

const container = document.getElementById('root')

ReactDOM.render(<App name="yceffort blog" />, container)

ReactDOM.render(<App name="yceffort post" />, container)

 

after

import ReactDOM from 'react-dom'
import App from 'App'

const container = document.getElementById('root')

// 루트 생성
const root = ReactDOM.createRoot(container)

// 최초 렌더링
root.render(<App name="yceffort blog" />)
// During an update, there is no need to pass the container again
// 업데이트 시에는, container를 다시 넘길 필요가 없음
root.render(<App name="yceffort post" />)

 

 

▶ hydrateRoot(domNode, reactNode, options?)

hydrateRoot는 서버사이드 렌더링 애플리케이션에서 hydrate하기 위한 새로운 메소드

이전에 react-dom/server로 생성한 HTML 콘텐츠가 있는 브라우저 DOM 노드 내에 React 컴포넌트를 표시할 수 있음

새로운 React DOM Server API와 함께 ReactDOM.hydrate 대신 사용하면 됨

리액트 18의 새로운 기능은 이와 함께 작동하지 않음

 

import { hydrateRoot } from 'react-dom/client';

const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, reactNode);

 

파라미터

  • domNode : 서버에서 루트 요소로서 렌더링 된 DOM 요소.
  • reactNode : 기존에 존재하는 HTML을 렌더링 하기 위해 사용되는 "리액트 노드"
    일반적으로 renderToPipeableStream(<App />) 같은 ReactDOM Server메서드로 렌더링 된 App 컴포넌트 같은 JSX 조각들
  • options : 이 리액트 root을 위한 옵션들을 가진 객체입니다.
  1. optional onRecoverableError : 에러들을 리액트가 자동으로 해결하려 할때 호출 되는 콜백 함수
  2. optional identifierPrefix : 리액트가 useId를 사용해서 만들기 위한 ID에 쓰이는 문자열 접두사
    다수의 같은 페이지의 root들을 사용할때 충돌을 피하기 위해 사용됩니다. 서버에서 사용되는 접두사와 같아야 함

주의사항

  • hydrateRoot() 은 렌더링 된 컨텐츠가 서버에서 렌더링 된 컨텐츠들과 동일할 것이라고 예상하지만 만약 불일치 하다면 버그로 취급하고 수정해야 함
  • 개발모드에서, 리액트는 하이드레이션 동안에 이 불일치에 대해서 경고를 띄움. 이러한 불일치의 경우에서 차이점들을 자동으로 일치화 해준다는 보장이 없음. 이것은 성능의 이유에서 정말 중요함. 왜냐하면 대부분의 앱에서 불일치는 거의 없고 이를 검증하는데는 상당한 비용이 소모됨
  • 보통 한개의 hydrateRoot을 앱 안에서 호출할 것임. 만약에 프레임워크를 사용한다면, 자동으로 불러와짐
  • 이미 렌더링 된 HTML가 없는 클라이언트 렌더링된 앱이라면, hydrateRoot 은 지원되지 않음. 대신 createRoot()을 사용

 

이전 버전과 현재 버전 차이

before

import ReactDOM from 'react-dom'
import App from 'App'

const container = document.getElementById('root')

ReactDOM.hydrate(<App name="yceffort blog" />, container)

 

after

import ReactDOM from 'react-dom'
import App from 'App'

const container = document.getElementById('root')

const root = ReactDOM.hydrateRoot(container, <App name="yceffort blog" />)

onRecoverableError를 옵션으로 받을 수 있는데 리액트가 렌더링이나 hydration시 에러가 발생하여 리커버리를 시도할 때 logging을 할 수 있는 목적으로 제공됨. 기본값으로 reportError나 구형 브라우저에서는 console.error를 씀

 

[ React DOM Server ]

react-dom/server에 새로운 API가 추가되었으며, 이는 서버에서 streaming Suspense를 완벽하게 지원함

▶ renderToPipeableStream(reactNode, options?)

renderToPipeableStream은 node 환경에서 스트리밍 지원. React 트리를 pipeable Node.js 스트림으로 렌더링함

클라이언트에서 hydrateRoot를 호출하여 서버에서 생성된 HTML을 상호작용이 가능하도록 할 수 있음

이 API는 Node.js에만 해당됨

Deno 및 최신 엣지 런타임과 같은 웹 스트림이 있는 환경에서는 대신 renderToReadableStream을 사용해야 함

 

import { renderToPipeableStream } from 'react-dom/server';

const { pipe } = renderToPipeableStream(<App />, {
  bootstrapScripts: ['/main.js'],
  onShellReady() {
    response.setHeader('content-type', 'text/html');
    pipe(response);
  }
});
  • <Suspense>와 함께 사용 가능
  • 콘텐츠가 잠시 사라지는 문제없이 lazy와 함께 코드 스플리팅 가능
  • 지연된 콘텐츠 블록이 있는 HTML 스트리밍이 나중에 뜰 수 있음

 

▶ renderToReadableStream(reactNode, options?)

renderToReadableStream은 Cloudflare, deno와 같이 모던 엣지 런타임 환경에서 스트리밍 지원

클라이언트에서 hydrateRoot를 호출하면 서버에서 생성된 HTML을 상호작용이 가능하도록 만듬

renderToString는 여전히 존재하지만, 사용하는 것이 권장되지는 않음

import { renderToReadableStream } from 'react-dom/server';

async function handler(request) {
  const stream = await renderToReadableStream(<App />, {
    bootstrapScripts: ['/main.js']
  });
  return new Response(stream, {
    headers: { 'content-type': 'text/html' },
  });
}

Cloudflare, deno와 같이 모던 엣지 런타임 환경에서 스트리밍 지원

클라이언트에서 hydrateRoot를 호출하면 서버에서 생성된 HTML을 상호작용이 가능하도록 만듬

renderToString는 여전히 존재하지만, 사용하는 것이 권장되지는 않음