4. Bingact Functional component

Circlewee·2022년 6월 22일
0

WYLSBingsu

목록 보기
5/8
post-thumbnail

0. 지난번에 이어서

지난번을 다시 돌아보자면

  1. 재조정(Reconciliation) 알고리즘 구현
    이것을 통해 React가 어떤 방식으로 변경 점만 재 렌더링할 수 있는지 알아볼 수 있었다.
  2. 이벤트 핸들러 삭제, 등록 구현

을 만들었다.
이번엔 React의 핵심 기능 중 하나인 함수형 컴포넌트와 useState()훅을 구현해보려고 한다.

1. 함수형 컴포넌트

React 16.8버전부터 추가된 기능으로 기존의 class형 컴포넌트만 존재하던 것에서 추가된 기능이다.
class형 컴포넌트의 lifecycle을 hook을 통해 구현할 수 있으며 기본으로 존재하는 훅외에 개발자가 필요한 훅을 정의해서 자신만의 effect를 만들어 사용할 수도 있다.

그렇다면 왜 함수형 컴포넌트의 장점은 무엇일까?

1.1 장점

  1. 값의 불변성을 보장받는다.
    이것은 클래스형 문법의 핵심 포인트인 this의 가변성때문이다. 컴포넌트에서의 props는 불변성 항목이지만 class형 컴포넌트에서 this는 가변성때문에 사용하는 것이다.
    따라서 함수형 컴포넌트와 달리 클로져를 활용한 별도의 추가 선언(변수에 할당 등)이 필요하다.
    이는 여러 개의 state를 관리하거나 같은 로직을 여러번 작성하게 되는 번거로움이 생길 수 있다.
  2. class의 문법은 함수보다 어렵다.
    위에서 등장하는 this의 개념도 있지만 컴포넌트마다 매 lifecycle에 따른 메소드를 정의해줘야하는 번거로움도 있다.

1.2 그래서 클래스형 컴포넌트는 뒤쳐진 문법인가?

정답부터 말하면 당연히 아니다. 함수형 컴포넌트는 위에서도 서술했듯이 16.8버전부터 등장한 개념이다. 이전에 개발된 서비스들의 경우에는 모두 클래스형 컴포넌트를 사용하고 있고 이를 나중에 함수형으로 리팩토링하던 클래스로 개발을 이어나가던 둘의 개념을 모두 알아둬야만 헤매지 않고 개발할 수 있을 것이다.

그리고 React팀도 클래스형 컴포넌트의 개념을 삭제하지 않고 함수형 컴포넌트만을 위한 새로운 문법을 추가하는 것이 아니라 클래스형 컴포넌트에서도 지원되게 개발을 이어나갈 예정이라고 한다.

1.3 구현

먼저 지금까지의 코드에서 함수형 컴포넌트가 지원되지 않는 데에는 두 가지 이유가 있다.

// 1
function performUnitOfWork(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber);
  }
  // 2
  const elements = fiber.props.children
  reconcileChildren(fiber, elements)
  ...
  1. 함수형 컴포넌트에서 만들어진 fiber는 DOM 노드가 없다.
  2. 자식들(children)을 props에서 직접 가져오는 대신 함수를 실행하여 얻는다.

error caps

dom을 형성하는 과정에서 우리가 작성한 <Container />, <MyComponent />같은 function type은 존재하지 않기 때문에 해당 과정에서 오류가 발생하게 된다. 따라서 이 부분을 수정해줘야한다.

function performUnitOfWork(fiber) {
  const isFunctionComponent = fiber.type instanceof Function;

  if (isFunctionComponent) {
    updateFunctionComponent(fiber);
  } else {
    updateHostComponent(fiber);
  }
  ...
}

function updateFunctionComponent(fiber) {
  const children = [fiber.type(fiber.props)];
  reconcileChildren(fiber, children);
}

// 기존 jsx컴포넌트 렌더링(변수 컴포넌트)
function updateHostComponent(fiber) {
  if (!fiber.dom) {
    fiber.dom = createDom(fiber);
  }
  reconcileChildren(fiber, fiber.props.children);
}

먼저 fiber.type의 prototype에 Function이 존재하는지 확인하고 그 여부에 따라 분기한다.
updateHostComponent()는 기존의 로직을 따로 분리한 것이다.
updateFunctionComponent()에서는 함수에서 반환하는 element들이 곧 children이므로 이를 가져와 기존처럼 재조정과정을 거치면 된다.
여기서 type()으로 사용할 수 있는 이유는 type이 function이기 때문이다.


function commitWork(fiber) {
  if (!fiber) {
    return;
  }
  // const domParent = fiber.parent.dom;
  let domParentFiber = fiber.parent;

  while (!domParentFiber.dom) {
    domParentFiber = domParentFiber.parent;
  }
  const domParent = domParentFiber.dom;

다음으로 commitWork()을 수정한다. 함수형 컴포넌트가 함수형 컴포넌트를 반환하는 경우에는 더 상위 부모 fiber를 찾는 과정이 필요하므로 반복문을 추가해준다.

  ...
  } else if (fiber.effectTag === 'DELETION') {
    // 삭제
    // domParent.removeChild(fiber.dom);
    commitDeletion(fiber, domParent)
  }
  ...
function commitDeletion(fiber, domParent) {
  if (fiber.dom) {
    domParent.removeChild(fiber.dom)
  } else {
    commitDeletion(fiber.child, domParent)
  }
}

그리고 재조정 과정에서 삭제하는 부분도 dom의 존재 여부를 확인해 존재하면 삭제하게끔 수정해준다.

함수형 컴포넌트를 사용하기위한 모든 준비는 끝났다! 우리가 React에서 평소에 사용하던대로 component를 구현하면 올바르게 렌더링 되는 것을 확인할 수 있다.

1.4 해치웠나??

const Hello = (props) => (
  <div id='bar'>
    <h1>HI {props.world}</h1>
  </div>
);

function FuncElement() {
  return (
    <div>
      <Hello world='world' />
    </div>
  );
}

const container = document.getElementById('root');
Bingact.render(<FuncElement />, container);

very good!

해치웠다!!

2. useState()

이제 React의 기본 구현은 마무리 되었다. 남은 것은 핵심 기능인 Hook구현이다.
그래서 제일 중요한 훅 중 하나인 useState()를 구현해보려한다.


구현해보려 했으나 결국 구현하지 않았다.
이유는 여러가지가 있지만 제일 큰 이유는 프로젝트 완성까지 시간이 얼마남지 않았다는 것과 이 Bingact를 단순 TODO list정도를 구현하는 것에서 끝나는 것이 아닌 SPA Routing과 성능을 고려하였을 때 불가능할 것 같다는 생각이 들어서이다.
이 시리즈의 마무리는 다음 글에서 왜 그만 두게 되었는지 얘기해보려 한다.

Reference
함수형 컴포넌트와 클래스

profile
공부할 게 너무 많아요

0개의 댓글