렌더링 예약하기

아래 코드가 실행되면 결과적으로는 react-reconciler의 createContainerupdateContainer 함수가 호출됨을 지난 게시물에서 확인했다.

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

이제 ReactDOM은 잊고 react-reconciler/src/ReactFiberReconciler.js 파일에 있는 두 함수를 살펴보자.

createContainer

트리를 구성하는 노드 두 개를 만든다.

export function createContainer(
  containerInfo: Container,
  ...
): OpaqueRoot {
  return createFiberRoot(
    containerInfo,
    ...
  );
}

export function createFiberRoot(
  containerInfo: Container,
  ...
): FiberRoot {
  const root: FiberRoot = (new FiberRootNode(
    containerInfo,
    ...
  ): any);

  // 두 노드가 서로에게 접근할 수 있다.
  const uninitializedFiber = createHostRootFiber(tag, isStrictMode);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;

  return root;
}

직접 확인해보자:

FiberRootNodeHostRootFiber가 등장했다. 이름이 헷갈리는데 각각 무엇인지 살펴보자.

FiberRootNode

function FiberRootNode(
  // 호출할 때는 없었던 this 인자가 생겼다. 
  // 파이썬 self랑 비슷한건가? Flow의 기능일지도... 모르겠다.
  this: $FlowFixMe,

  // 우리가 건넨 document.getElementById("root")
  containerInfo: any,
  ...
) {
  this.tag = disableLegacyMode ? ConcurrentRoot : tag;
  this.containerInfo = containerInfo;
  this.pendingChildren = null;
  this.current = null;
  ...
  // 열심히열심히 this.xx = yy 하는 코드들...
}

FiberRootNodecreateRoot 함수에서 건넨 컨테이너 DOM 객체를 관리한다.

HostRootFiber

createHostRootFiber 함수는 HostRoot 태그를 가진 Fiber를 만든다.

export function createHostRootFiber(
  tag: RootTag,
  isStrictMode: boolean,
): Fiber {
  ...
  return createFiber(HostRoot, null, null, mode);
}

FiberRootNodeHostRootFiber 모두 이름에 Fiber가 들어가지만 FiberRootNode는 Fiber의 한 종류라기보단 Fiber 트리의 루트와 관련된 메타데이터를 담고 있는 다른 종류의 객체라고 이해하자.

Fiber가 중요해보이니 createFiber를 타고 들어가 Fiber의 구조를 살펴보자.

function FiberNode(
  this: $FlowFixMe,
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;
  ...
  // 열심히열심히 this.xx = yy 하는 코드들...
}

자세히는 보지 않겠지만 암튼 WorkTag란걸로 Fiber의 종류를 구분함을 알 수 있다. HostRootFiber는 HostRoot 태그를 가진다.

WorkTag 타입을 구경가보면 이런게 있다:

export const FunctionComponent = 0;
export const ClassComponent = 1;
// 2는 왜 없지
export const HostRoot = 3;
export const HostPortal = 4;
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
...

지금까지 살펴본걸 직접 로그찍어보며 createContainer 살펴보는걸 마무리하자.

updateContainer

updateContainerImpl을 호출한다. 리액트 소스코드에는 이런 식으로 함수 + impl함수 식으로 구현된 경우가 많다.

// react-reconciler/src/ReactFiberReconciler.js
export function updateContainer(
  element: ReactNodeList, // 우리가 건넨 <App/>
  container: OpaqueRoot, // FiberRootNode
  ...
): Lane {
  // HostRootFiber임을 아까 살펴봤다.
  const current = container.current;
  const lane = requestUpdateLane(current);
  updateContainerImpl(
    current,
    lane,
    element,
    container,
    parentComponent,
    callback,
  );
  return lane;
}

업데이트 객체를 만들어 enqueueUpdate로 업데이트를 예약한다. 큐의 payload에는 우리가 건넨 <App/>이 들어있다.

function updateContainerImpl(
  rootFiber: Fiber, // HostRootFiber
  lane: Lane,
  element: ReactNodeList, // 우리가 건넨 <App/>
  container: OpaqueRoot, // FiberRootNode
  ...
): void {

  // 우리가 건넨 <App/>을 업데이트 큐에 넣는다.
  const update = createUpdate(lane);
  update.payload = {element};

  const root = enqueueUpdate(rootFiber, update, lane);

  if (root !== null) {
    startUpdateTimerByLane(lane);
    scheduleUpdateOnFiber(root, rootFiber, lane);
    entangleTransitions(root, rootFiber, lane);
  }
}

업데이트 큐가 어떻게 관리되는지는 나~중에 삺펴보자.

이전글목록으로다음글

로그인 중...

seongyeol