아래 코드가 실행되면 결과적으로는 react-reconciler의 createContainer
와 updateContainer
함수가 호출됨을 지난 게시물에서 확인했다.
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
이제 ReactDOM은 잊고 react-reconciler/src/ReactFiberReconciler.js
파일에 있는 두 함수를 살펴보자.
트리를 구성하는 노드 두 개를 만든다.
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;
}
직접 확인해보자:
FiberRootNode
와 HostRootFiber
가 등장했다. 이름이 헷갈리는데 각각 무엇인지 살펴보자.
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 하는 코드들...
}
FiberRootNode
는 createRoot
함수에서 건넨 컨테이너 DOM 객체를 관리한다.
createHostRootFiber
함수는 HostRoot 태그를 가진 Fiber를 만든다.
export function createHostRootFiber(
tag: RootTag,
isStrictMode: boolean,
): Fiber {
...
return createFiber(HostRoot, null, null, mode);
}
FiberRootNode
와 HostRootFiber
모두 이름에 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
살펴보는걸 마무리하자.
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);
}
}
업데이트 큐가 어떻게 관리되는지는 나~중에 삺펴보자.
로그인 중...