본문 바로가기
FRONT-END/React

나만의 React 만들기(Build your own react) - STEP 2

by 랄라J 2024. 9. 4.

https://pomb.us/build-your-own-react/ 을 기반으로 학습 내용을 정리한 글입니다.


STEP 2 - The render function

STEP1 코드에 이어 이제 render function을 만들어보자.
ReactDOM.render 버전을 Didact.render로 만들어보는 것이 이번 step의 목표다.

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((child) =>
        typeof child === "object" ? child : createTextElement(child)
      ),
    },
  };
}

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: [],
    },
  };
}

const Didact = {
  createElement,
};

/** @jsx Didact.createElement */
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
);

const container = document.getElementById("root");

위 코드까지가 STEP1 코드였다.

 

이제 render 함수 정의하고, render를 호출하도록 코드를 작성해 보자.
우선 해당하는 부분의 코드 틀은 아래와 같다!

function render(element, container) {
  // TODO create dom nodes
}

const Didact = {
  createElement,
  render,
};

/** @jsx Didact.createElement */
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
);

const container = document.getElementById("root");
Didact.render(element, container);

 

render 구현에서는 이전에 dom에 추가하는 코드를 썼던 부분을 활용할 것이다.

const node = document.createElement(element.type);
node["title"] = element.props.title;

const text = document.createTextNode("");
text["nodeValue"] = element.props.children;

node.appendChild(text);
container.appendChild(node);

 

render에서 받아 처리할 element 형태는 이전 step에서 확인해 봤듯 아래의 형태라는 걸 인지하면 이어지는 코드를 이해하기 쉽다.

[
  { type: "TEXT_ELEMENT", props: { nodeValue: "Hello", children: [] } },
  { type: "TEXT_ELEMENT", props: { nodeValue: "World", children: [] } },
  { type: "TEXT_ELEMENT", props: { nodeValue: "123", children: [] } },
];

 

우선 ReactDOM.render은 element, container 2가지 인수를 받았다.
동일하게 구현해 주기 위해 render도 element, container 2가지 인수
그리고 element를 하나의 node, 여기서는 dom이라 변수에 넣은 뒤 container 하위에 추가해 주면 된다.

function render(element, container) {
  const dom = document.createElement(element.type);

  container.appendChild(dom);
}

 

그리고 element에서 props로 들어오는 부분에 대해서도 추가해 주는 처리의 코드를 추가한다.
재귀를 사용해 render를 다시 호출해 주는 방법으로 처리되었다.

function render(element, container) {
  const dom = document.createElement(element.type);

  element.props.children.forEach((child) => render(child, dom));

  container.appendChild(dom);
}

 

children으로 들어오는 element의 type이 TEXT_ELEMENT인 경우 createElement가 아닌 createTextNode로 처리해 주었다.

function render(element, container) {
  const dom =
    element.type == "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(element.type);

  element.props.children.forEach((child) => render(child, dom));

  container.appendChild(dom);
}

 

마지막으로 element props를 node에 할당하면 된다!

function render(element, container) {
  const dom =
    element.type == "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(element.type);

  const isProperty = (key) => key !== "children";
  Object.keys(element.props)
    .filter(isProperty)
    .forEach((name) => {
      dom[name] = element.props[name];
    });

  element.props.children.forEach((child) => render(child, dom));

  container.appendChild(dom);
}

 

그럼 step2 최종 코드는 아래와 같다.

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((child) =>
        typeof child === "object" ? child : createTextElement(child)
      ),
    },
  };
}

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: [],
    },
  };
}

function render(element, container) {
  const dom =
    element.type === "TEXT_ELEMENT"
      ? document.createTextNode("")
      : document.createElement(element.type);

  const isProperty = (key) => key !== "children";
  Object.keys(element.props)
    .filter(isProperty)
    .forEach((name) => {
      dom[name] = element.props[name];
    });

  element.props.children.forEach((child) => render(child, dom));

  container.appendChild(dom);
}

const Didact = {
  createElement,
  render,
};

/** @jsx Didact.createElement */
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
);

const container = document.getElementById("root");
Didact.render(element, container);

 

실행에 이상이 있다면, 본문에서 제공해 주는 step2까지의 codesandbox와 비교하자!

나도 실행에 이상이 있었는데, 보니까 react가 16 버전이 아니어서 다시 npm 설치를 진행했고,
Node.js버전과 OpenSSL 라이브러리 호환성 이슈로 node.js 버전을 nvm 통해 16으로 낮췄다.

그리고 실행하니 정상 실행되었다 :)


여기까지 STEP 2 완료!

반응형

댓글