[번역] React가 빈 값을 다루는 것은 어떻게 동작하나요?

[번역] React가 빈 값을 다루는 것은 어떻게 동작하나요?

React Internals Deep Dive - EP18

·

3 min read

영문 블로그 글을 번역했습니다. 허가를 받으면 시리즈를 이어갈 예정입니다. 원문링크: https://jser.dev/react/2022/02/04/how-React-handles-empty-values


ℹ️React Internals Deep Dive 에피소드 18, 유튜브에서 제가 설명하는것을 시청해주세요.

⚠React@18.2.0기준, 최신 버전에서는 구현이 변경되었을 수 있습니다.

💬 역자 주석: JSer의 코멘트는 ❗❗로 표시 해뒀습니다.
그 외 주석은 리액트 소스 코드 자체의 주석입니다.
... 은 생략된 코드입니다.

Booleans, Null, Undefined는 무시된다는 것을 모두 알고 계시겠지만, 아래와 같은 예제는 모두 동일하게 렌더링합니다.

<div />
<div></div>
<div>{false}</div>
<div>{null}</div>
<div>{undefined}</div>
<div>{true}</div>

하지만 이러한 값은 React 내부에서 정확히 어떻게 처리될까요? 알아봅시다.

이러한 값은 컴포넌트가 아니어서 일부 컴포넌트의 자식으로만 존재하므로 reconcileChildren()을 사용해 보겠습니다.

ℹ 이 포스팅은 저도 배우고 있는 시리즈의 일환이기도 한데, 당황스러운 부분이 있으시다면 먼저 제 과거 포스팅이나 영상을 보시면 될 것 같습니다. 위와 같이 이미 알고 있는 함수이기 때문에 그냥 넘어가는데, 그 이유를 설명하기는 어렵습니다.

reconcileChildren()에서 첫 번째 렌더링에는 mountChildFibers()가, 이후 렌더링에는 reconcileChildFibers()가 사용됩니다.

하지만 실제로 이 두 기능은 부작용을 추적할지 여부만 다를 뿐 동일합니다.

(소스)

export const reconcideChildFibers  = ChildReconciler(true);
export const mountChildFibers  = ChildReconciler(false);

ChildReconciler()에서 side effect는 '삭제'를 의미한다는 것을 알 수 있습니다.

ChildReconciler()는 위의 플래그 아래 몇 가지 클로저 함수로 구성되며, 실제 reconcileChildFibers()를 내보냅니다(export).

function reconcileChildFibers(
  returnFiber: Fiber,
  currentFirstChild: Fiber | null,
  newChild: any,
  lanes: Lanes
): Fiber | null {
  if (typeof newChild === "object" && newChild !== null) {
    switch (newChild.$$typeof) {
      case REACT_ELEMENT_TYPE:
        return placeSingleChild(
          reconcileSingleElement(
            returnFiber,
            currentFirstChild,
            newChild,
            lanes
          )
        );
      case REACT_PORTAL_TYPE:
        return placeSingleChild(
          reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes)
        );
      case REACT_LAZY_TYPE:
        if (enableLazyElements) {
          const payload = newChild._payload;
          const init = newChild._init;
          // TODO: This function is supposed to be non-recursive.
          return reconcileChildFibers(
            returnFiber,
            currentFirstChild,
            init(payload),
            lanes
          );
        }
    }
    if (isArray(newChild)) {
      return reconcileChildrenArray(
        returnFiber,
        currentFirstChild,
        newChild,
        lanes
      );
    }
    if (getIteratorFn(newChild)) {
      return reconcileChildrenIterator(
        returnFiber,
        currentFirstChild,
        newChild,
        lanes
      );
    }
    throwOnInvalidObjectType(returnFiber, newChild);
  }
  if (
    (typeof newChild === "string" && newChild !== "") ||
    typeof newChild === "number"
  ) {
    return placeSingleChild(
      reconcileSingleTextNode(
        returnFiber,
        currentFirstChild,
        "" + newChild,
        lanes
      )
    );
  }
  // Remaining cases are all treated as empty.
  return deleteRemainingChildren(returnFiber, currentFirstChild);

이 것은 4단계로 구성되어 있습니다.

  1. $$typeof를 기반으로 단일 엘레먼트 타입을 처리합니다.

  2. 배열 또는 이터레이터를 처리합니다.

  3. 비어 있지 않은 문자열 및 숫자

  4. 나머지는 비어있는 것(empty)으로 처리되어 이전 파이버가 있으면 삭제됩니다.

따라서 파이버를 만들 때 null, undefined 및 boolean은 단순히 무시된다는 것을 알 수 있습니다.

배열에 있는 경우는 어떨까요, reconcideChildrenArray()를 살펴보겠습니다.

reconcideChildrenArray() 에는 나중에 다루고 싶은 몇 가지 알고리즘이 있습니다.

코드를 살펴보면 새로운 파이버가 생성될 수 있는 두 곳을 확인할 수 있습니다.

const newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], lanes);
const newFiber = updateFromMap(
  existingChildren,
  returnFiber,
  newIdx,
  newChildren[newIdx],
  lanes
);

reconcileChildrenArray()에서는 배열 항목을 반복하여 새로운 링크드 파이버 리스트를 구성합니다.

newFiber가 null이면 단순히 무시되고, 파이버 트리에 추가되지 않습니다.

updateSlot()updateFromMap()에서 빈 값이 무시되고 null이 반환되는 유사한 패턴을 발견할 수 있습니다.

function updateSlot(
  returnFiber: Fiber,
  oldFiber: Fiber | null,
  newChild: any,
  lanes: Lanes,
): Fiber | null {
  if (
    (typeof newChild === 'string' && newChild !== '') ||
    typeof newChild === 'number'
  ) {
    ...
  }
  if (typeof newChild === 'object' && newChild !== null) {
    ...
  }
  return null;
}

그게 다입니다. 이제 React에서 빈 값이 어떻게 처리되는지 알게 되었습니다. - 이것들은 간단하게 무시됩니다.

한 가지 작은 문제는 실제로 reconcileChildrenArray()의 조정(reconciling) 알고리즘에 영향을 미친다는 것인데, 이에 대해서는 곧 포스팅을 작성할 예정이니 계속 지켜봐 주시기 바랍니다.

(원본 게시일: 2022-02-04)