- 업데이트를 반영할 Work를 Scheduler에게 전달하고 scheduler는 스케쥴링된 Task를 적절한 시기에 실행한다
- Reconciler는 비동기 호출이 필요한 Work의 실행 제어권을 해당 분야의 전문가인 Scheduler에게 위임한다
- Reconciler가 스스로 판단해서 실행하는 게 아닌 scheduler가 브라우저의 상태와 여러 조건을 기반으로 적절한 시기를 판단해서 실행하고 있다
- Scheduling Flow
- Reconciler
- VDOM 재조정 작업 전에 설정해줘야 하는 부분들을 처리
- Scheduler
- 스케쥴링 된 Task에 우선순위 반영, 실행하기 적절한 때를 판단하고 작업의 실행과 중단을 담당
- Scheduler에는 host config라는 모듈이 존재하는데, 호스트 환경에 의존적인 api를 사용하는 모듈이다
- 여기에는 비동기 api, performance, isInputPending 등이 있다
- isInputPending
1. dispatchAction
// reconciler > ReactFiberHooks.js
function dispatchAction(...) {
if (...) {
/* Render phase Update ... */
} else {
/* idle update ... */
scheduleWork(fiber, expirationTime);
}
}
scheduleWork
- Work를 스케쥴링 하기 전에 reconciler에서 해야 할 일
- 해당 컴포넌트에서 이벤트가 발생했음을 알리는 expirationTime을 새긴다
- 이벤트가 발생한 컴포넌트의 VDOM root를 가지고 온다
- root에 스케쥴 정보를 기록한다
- 현 상황에서 root의 의미
- 리액트를 레거시 프로젝트에 도입한다고 했을 때 ReactDOM.render()를 통해 컴포넌트를 삽입한다면 영역별로 root가 생성된다
- 그리고 root마다 VDOM도 같이 생성된다
- root는 VDOM을 대표하는 변하지 않는 객체이다
- 그래서 root에는 많은 정보가 기입되는데 그 중 하나가 스케쥴 정보이다
- 단일 VDOM에 여러 업데이트가 발생하여 복수의 Work 스케쥴링 요청이 들어온다고 했을 때
- 이 때 요청이 들어온 Work와 이미 스케쥴링된 Work 사이에 교통정리가 필요함
- 이 역할을 reconciler가 해주고, 교통정리의 기준이 되는 게 root에 새겨진 스케쥴 정보이다
2-1 expirationTime 새기기
// reconciler > ReactFiberWorkLoop.js
export function scheduleUpdateOnFiber(fiber, expirationTime) {
const root = markUpdateTimeFromFiberToRoot(fiber, expirationTime)
}
export const scheduleWork = scheduleUpdateOnFiber;
- 먼저 업데이트가 발생한 fiber에 expirationTime을 새긴다
- 그리고 root를 찾기 위해 위로 올라가는데, 거쳐 가는 fiber에도 시간을 새긴다
- 이 때는 expirationTime이 아닌 자손에서 이벤트가 발생했음을 나타내는 childExpirationTime을 새긴다
// reconciler > ReactFiberWorkLoop.js
function markUpdateTimeFromFiberToRoot(fiber, expirationTime) {
// expirationTime을 새긴다
if (fiber.expirationTime < expirationTime) {
fiber.expirationTime = expirationTime
}
let alternate = fiber.alternate
if (alternate !== null && alternate.expirationTime < expirationTime) {
alternate.expirationTime = expirationTime
}
// root를 찾는다
let node = fiber.return
let root = null
if (node !== null && fiber.tag === HostRoot) {
root = fiber.stateNode // Host root의 stateNode가 root이다.
} else {
while (node !== null) {
alternate = node.alternate
// childExpirationTime을 새긴다
if (node.childExpirationTime < expirationTime) {
node.childExpirationTime = expirationTime
if (
alternate !== null &&
alternate.childExpirationTime < expirationTime
) {
alternate.childExpirationTime = expirationTime
}
} else if (
alternate !== null &&
alternate.childExpirationTime < expirationTime
) {
alternate.childExpirationTime = expirationTime
}
if (node.return === null && node.tag === HostRoot) {
root = node.stateNode
break
}
node = node.return
}
}
return root
}