본문 바로가기
source-code/React

react-dom의 root.render()를 여러 번 호출하면 어떤 일이 생길까?

by mattew4483 2024. 1. 4.
728x90
반응형

현재 회사에서 chrome extension을 React를 사용해 개발하고 있다.

extension에서는 manifest.json의 content_scripts를 통해, 실행 시 자동으로 js 파일을 동작하게 할 수 있다.

→ 실행할 content-sciprts 파일에서 react를 사용해 페이지를 그려주면 되는 것.

// content-script.ts
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

const rootElement = document.createElement("div");
document.body.appendChild(rootElement);

const root = createRoot(rootElement);
root.render(<App />);

해당 js파일을 실행해 사용자의 웹페이지에 우리의 애플리케이션(App컴포넌트)이 보여야 하기 때문에

element를 생성해 body에 appendChild한 후,

react-dom의 createRoot api를 사용해 Root를 생성한 뒤

App이라는 JSX를 render했다.

 

그런데, 오늘 다른 팀원이 이런 코드를 작성했다.

// content-script.ts
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

const rootElement = document.createElement("div");
document.body.appendChild(rootElement);

const root = createRoot(rootElement);
root.render(<App />); // 최초 Root.render 발생

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "open") {
    root.render(<App />); // 두번째 Root.render 발생
  } else {
    root.render(<></>);
  }
});

chrome의 onMessage api를 통해 sendMessage를 수신하여

open 이벤트 발생 시 애플리케이션을 화면에 그리고, 그렇지 않은 경우 삭제하는 로직.

 

물론 이후 수정되었지만, 처음 위 수정사항을 보았을 때 이런 의문이 들었다.

runtime에서 open 이벤트를 수신할 경우 root.render는 content-script가 실행될 때 한 번, 이벤트 수신 시 한 번.

이렇게 총 두 번 실행되는데...

→ 화면에 우리가 작성한 App 컴포넌트가 두 개 생성되는 건 아닐까?

 

정답은... 아니다!

runtime에서 open 이벤트를 여러 번 발생시켜도, App 컴포넌트가 여러 개 생성되지는 않는다. 왜?

→ 늘 그렇듯 공식 문서를 보면 된다.

https://react.dev/reference/react-dom/client/createRoot#ive-created-a-root-but-nothing-is-displayed

 

createRoot – React

The library for web and native user interfaces

react.dev

createRoot(domNode, options?) 

createRoot 실행 시 Root 타입의 객체를 반환받는다.

export interface Root {
    render(children: React.ReactNode): void;
    unmount(): void;
}

해당 타입 자체는 별다를 게 없지만...

Call createRoot to create a React root for displaying content inside a browser DOM element.

해당 객체를 통해 browser의 DOM 요소에 content를 띄워줄 수 있는 것!

 

createRoot api를 통해 전달받은 domNode를 위한 root를 생성하고,

React는 이렇게 생성된 root 내부의 DOM을 계속해서 관리(manage)하게 된다.

(일반적인 React application은 root가 하나지만

페이지 일부에 React를 위한 sprinkles를 사용하는 페이지의 경우, 여러 개의 root가 존재할 수도 있다)

 

root.render(reactNode) 

이렇게 생성된 root의 render 메서드를 통해, 해당 dom 내부에 우리가 만든 React 컴포넌트를 display 할 수 있다.

root.render(<App />);

React는 root 내에 App 컴포넌트를 display 하고, 내부의 DOM을 지속적으로 관리한다.

 

그런데 이때 중요한 점은...

1. 처음 root.render를 호출할 경우, React는 입력받은 컴포넌트를 렌더링 하기 전 root 내 존재하는 HTML 요소들을 초기화한다.

2. 만약 동일한 root에 render를 여러 번 호출하는 경우, 마지막으로 전달된 JSX를 반영해 필요한 경우에만 DOM을 업데이트한다.

 

즉 render가 다시 호출될 경우 React는 그 직전 JSX의 DOM에서 재사용 가능한 부분과 새로 생성할 부분을 결정하며

변경사항이 존재할 경우에만 이를 반영하는 것!

// content-script.ts
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";

const rootElement = document.createElement("div");
document.body.appendChild(rootElement);

const root = createRoot(rootElement);
root.render(<App />); // 최초 Root.render 발생

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === "open") {
    root.render(<App />); 
    // 두번째 Root.render 발생
    // onMessage 직전 App의 dom tree에 변경사항이 발생했을 경우에만, 요소들이 새로 생성
  } else {
    root.render(<></>);
  }
});

아하..! 이제 위 코드를 보고 들었던 의문, root.render()를 여러 번 호출하면 어떤 일이 생길까 를 대답할 수 있다.

→ 바로 직전 생성된 JSX의 DOM에 변경 사항이 발생했을 경우에는, 해당 요소들이 새로 생성된다.

→ 그렇지 않은 경우에는, 아무런 일도 일어나지 않는다.

 

그리고 이 JSX의 DOM에 변경 사항이 발생했다 를 판단하는 기준은

→ 이전에 배웠던 React의 비교(diffing) 알고리즘에 따라 결정될 테다!

 

참고

https://react.dev/reference/react-dom/client/createRoot#root-render

 

createRoot – React

The library for web and native user interfaces

react.dev

https://react.dev/learn/preserving-and-resetting-state

 

Preserving and Resetting State – React

The library for web and native user interfaces

react.dev

https://23life.tistory.com/184

 

Reconciliation (재조정)

React의 비교(diffing)알고리즘 why? render = 해당 함수가 처음부터 끝까지 실행되는 것 = React element tree를 만드는 것 state, props가 갱신되면 render()함수는 새로운 React element tree를 반환 React → 가장 효과

23life.tistory.com

 

728x90
반응형