[번역] React 파이버 트리 순회는 어떻게 동작하나요?

[번역] React 파이버 트리 순회는 어떻게 동작하나요?

React Internals Deep Dive - EP15

·

2 min read

영문 블로그 글을 번역했습니다. 허가를 받으면 시리즈를 이어갈 예정입니다.
원문링크:
https://jser.dev/react/2022/01/16/fiber-traversal-in-react


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

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

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

Fiber Tree 구조

앞서 설명했듯이, React는 내부적으로 파이버 트리를 유지하며 계속 업데이트합니다. 각 파이버 노드에는 구조를 형성하는 3가지 프로퍼티가 있습니다.

  1. child, 이름에 따라 그 자식 파이버.

  2. sibling, 이름에 따라 그 형제 파이버

  3. return, (일종의)부모 파이버라고 생각할 수 있습니다.

children이 없기 때문에, 제가 추측하기로 React 팀은 트리 탐색을 가능한 한 간단하게 처리하고 싶기에, 이 세 가지 프로퍼티는 충분히 잘 작동할 것입니다.

이런 일이 있을 수 있습니다.

Fiber Tree는 어떻게 순회하는가

파이버 트리를 순회해야 하는 많은 시나리오가 있습니다. 예를 들어, React가 파이버 트리를 순회하여 패시브 이펙트(예: useEffect())를 마운트하는 방법은 다음과 같습니다(소스).

export function commitPassiveMountEffects(
  root: FiberRoot,
  finishedWork: Fiber
): void {
  nextEffect = finishedWork;
  commitPassiveMountEffects_begin(finishedWork, root);
}
function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    const firstChild = fiber.child;
    if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
      ensureCorrectReturnPointer(firstChild, fiber);
      nextEffect = firstChild;
    } else {
      commitPassiveMountEffects_complete(subtreeRoot, root);
    }
  }
}
function commitPassiveMountEffects_complete(
  subtreeRoot: Fiber,
  root: FiberRoot
) {
  while (nextEffect !== null) {
    const fiber = nextEffect;
    if ((fiber.flags & Passive) !== NoFlags) {
      setCurrentDebugFiberInDEV(fiber);
      try {
        commitPassiveMountOnFiber(root, fiber);
      } catch (error) {
        reportUncaughtErrorInDEV(error);
        captureCommitPhaseError(fiber, fiber.return, error);
      }
      resetCurrentDebugFiberInDEV();
    }
    if (fiber === subtreeRoot) {
      nextEffect = null;
      return;
    }
    const sibling = fiber.sibling;
    if (sibling !== null) {
      ensureCorrectReturnPointer(sibling, fiber.return);
      nextEffect = sibling;
      return;
    }
    nextEffect = fiber.return;
  }
}

React는 begin()complete()각 노드를 두 번 밟기 때문에, 캡처와 버블 단계가 있는 DOM 이벤트라고 생각하면 되고, begin()capture 단계, complete( )는 bubble 단계라고 생각하면 됩니다.

부모 파이버 노드가 먼저 begin() 되지만 자식보다 나중에 complete() 됩니다.

이 접근 방식은 begincomplete의 포스트픽스(💬 _begin 이런 형식의 접미사 네이밍) 와 함께 React 소스 코드의 모든 곳에 있으므로, 깊이 빠지기 전에 이 접근 방식에 익숙해져야 합니다.

코딩 챌린지

알고리즘에 대한 이해를 높이기 위해 코딩 챌린지를 준비했습니다. 즐겨보세요!

JSer의 블로그 코딩 챌린지 링크

BFE.dev의 코딩 챌린지 링크

(원본 게시일: 2022-01-16)