React-reconciler | Fiber Stack (19.0.0)
HostRoot
, HostPortal
, HostSingleton
, HostComponent
, ContextProvider
, SuspenseComponent
, SuspenseListComponent
, OffscreenComponent
,LegacyHiddenComponent
,CacheComponent
관련 WorkTags에서
컨텍스트, 상태, 또는 기타 정보를 스택 구조를 사용해 추적하고 복원하는 데 사용됩니다. 가장 최근에 추가된 작업이 가장 먼저 처리되어야 하므로 스택 구조를 사용합니다.
react/packages/react-reconciler/src/ReactFiberStack.js
import type {Fiber} from './ReactInternalTypes';
const valueStack: Array<any> = [];
let index = -1;
export type StackCursor<T> = {current: T};
function createCursor<T>(defaultValue: T): StackCursor<T> {
return {
current: defaultValue,
};
}
function isEmpty(): boolean {
return index === -1;
}
function pop<T>(cursor: StackCursor<T>, fiber: Fiber): void {
if (index < 0) {
return;
}
cursor.current = valueStack[index];
valueStack[index] = null;
index--;
}
function push<T>(cursor: StackCursor<T>, value: T, fiber: Fiber): void {
index++;
valueStack[index] = cursor.current;
cursor.current = value;
}
export {
createCursor,
isEmpty,
pop,
push,
};
valueStack
각 작업 단계에서 저장된 컨텍스트 정보, 상태들을 저장하는 배열입니다.
index
스택의 현재 위치를 나타냅니다.
createCursor
값을 생성합니다.
isEmpty
스택이 비어있는지 확인합니다.
pop
값을 스택에서 제거하고 이전 값을 복구합니다.
push
값을 스택에 넣고 보존합니다.
ReactFiberBeginWork
react/packages/react-reconciler/src/ReactFiberBeginWork.js
function pushHostRootContext(workInProgress: Fiber) {
const root = (workInProgress.stateNode: FiberRoot);
if (root.pendingContext) {
pushTopLevelContextObject(
workInProgress,
root.pendingContext,
root.pendingContext !== root.context,
);
} else if (root.context) {
pushTopLevelContextObject(workInProgress, root.context, false);
}
pushHostContainer(workInProgress, root.containerInfo);
}
function updateHostRoot(
current: null | Fiber,
workInProgress: Fiber,
renderLanes: Lanes,
) {
pushHostRootContext(workInProgress);
...
pushRootTransition(workInProgress, root, renderLanes);
}
function updatePortalComponent(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
...
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
...
}
function updateHostComponent(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
...
pushHostContext(workInProgress);
...
}
function updateHostSingleton(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
...
pushHostContext(workInProgress);
...
}
function updateContextProvider(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
...
pushProvider(workInProgress, context, newValue);
...
}
function updateSuspenseComponent(
current: null | Fiber,
workInProgress: Fiber,
renderLanes: Lanes,
) {
...
if (current === null) {
if (getIsHydrating()) {
if (showFallback) {
pushPrimaryTreeSuspenseHandler(workInProgress);
} else {
pushFallbackTreeSuspenseHandler(workInProgress);
}
...
}
...
if (showFallback) {
pushFallbackTreeSuspenseHandler(workInProgress);
...
} else if (enableCPUSuspense && typeof nextProps.unstable_expectedLoadTime === 'number') {
pushFallbackTreeSuspenseHandler(workInProgress);
...
} else {
pushPrimaryTreeSuspenseHandler(workInProgress);
...
}
}
}
function updateDehydratedSuspenseComponent(
current: Fiber,
workInProgress: Fiber,
didSuspend: boolean,
didPrimaryChildrenDefer: boolean,
nextProps: any,
suspenseInstance: SuspenseInstance,
suspenseState: SuspenseState,
renderLanes: Lanes,
): null | Fiber {
if (!didSuspend) {
...
pushPrimaryTreeSuspenseHandler(workInProgress);
...
} else {
if (workInProgress.flags & ForceClientRender) {
pushPrimaryTreeSuspenseHandler(workInProgress);
...
} else if ((workInProgress.memoizedState: null | SuspenseState) !== null) {
pushFallbackTreeSuspenseHandler(workInProgress);
...
} else {
pushFallbackTreeSuspenseHandler(workInProgress);
...
}
}
}
function updateSuspenseListComponent(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
...
pushSuspenseListContext(workInProgress, suspenseContext);
...
}
function updateOffscreenComponent(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
...
if (
nextProps.mode === 'hidden' ||
(enableLegacyHidden &&
nextProps.mode === 'unstable-defer-without-hiding') ||
nextIsDetached
) {
if (
!disableLegacyMode &&
(workInProgress.mode & ConcurrentMode) === NoMode
) {
...
if (enableCache) {
if (current !== null) {
pushTransition(workInProgress, null, null);
}
}
pushOffscreenSuspenseHandler(workInProgress);
} else if (!includesSomeLane(renderLanes, (OffscreenLane: Lane))) {
...
} else {
if (enableCache && current !== null) {
...
pushTransition(workInProgress, prevCachePool, null);
}
if (prevState !== null) {
pushHiddenContext(workInProgress, prevState);
} else {
...
}
pushOffscreenSuspenseHandler(workInProgress);
}
} else {
if (prevState !== null) {
...
pushTransition(workInProgress, prevCachePool, transitions);
pushHiddenContext(workInProgress, prevState);
...
} else {
if (enableCache) {
if (current !== null) {
pushTransition(workInProgress, null, null);
}
}
}
}
...
}
function deferHiddenOffscreenComponent(
current: Fiber | null,
workInProgress: Fiber,
nextBaseLanes: Lanes,
renderLanes: Lanes,
) {
...
if (enableCache) {
if (current !== null) {
pushTransition(workInProgress, null, null);
}
}
pushOffscreenSuspenseHandler(workInProgress);
...
}
function updateCacheComponent(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
) {
...
if (current === null) {
...
pushCacheProvider(workInProgress, freshCache);
} else {
if (prevState.parent !== parentCache) {
...
pushCacheProvider(workInProgress, parentCache);
} else {
...
pushCacheProvider(workInProgress, nextCache);
...
}
}
...
}
HostRoot
HostPortal
HostSingleton
HostComponent
ContextProvider
SuspenseComponent
SuspenseListComponent
OffscreenComponent
LegacyHiddenComponent
CacheComponent
관련 업데이트가 있을 때 Fiber Stack에 관련 정보를 push
합니다.
completeWork
react/packages/react-reconciler/src/ReactFiberCompleteWork.js
function completeWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
...
switch (workInProgress.tag) {
...
case HostRoot: {
...
popCacheProvider(workInProgress, cache);
popRootMarkerInstance(workInProgress);
popRootTransition(workInProgress, fiberRoot, renderLanes);
popHostContainer(workInProgress);
...
}
case HostSingleton: {
...
popHostContext(workInProgress);
...
}
case HostComponent: {
...
popHostContext(workInProgress);
...
}
case SuspenseComponent: {
...
popSuspenseHandler(workInProgress);
...
}
case HostPortal:{
...
popHostContainer(workInProgress);
...
}
case ContextProvider:{
...
popProvider(context, workInProgress);
...
}
case SuspenseListComponent: {
...
popSuspenseListContext(workInProgress);
...
}
case OffscreenComponent:
case LegacyHiddenComponent: {
...
popSuspenseHandler(workInProgress);
popHiddenContext(workInProgress);
popTransition(workInProgress, current);
...
}
case CacheComponent: {
...
popCacheProvider(workInProgress, cache);
...
}
...
}
}
completeWork
은 작업이 성공적으로 완료되었을 때 호출되므로 Fiber Stack에 불필요한 메모리를 pop
합니다.
unwindWork
react/packages/react-reconciler/src/ReactFiberUnwindWork.js
function unwindWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
...
switch (workInProgress.tag) {
case HostRoot: {
...
popCacheProvider(workInProgress, cache);
popRootMarkerInstance(workInProgress);
popRootTransition(workInProgress, root, renderLanes);
popHostContainer(workInProgress);
popTopLevelLegacyContextObject(workInProgress);
...
}
case HostHoistable:
case HostSingleton:
case HostComponent: {
...
popHostContext(workInProgress);
...
}
case SuspenseComponent: {
...
popSuspenseHandler(workInProgress);
...
}
case SuspenseListComponent: {
popSuspenseListContext(workInProgress);
...
}
case HostPortal:
popHostContainer(workInProgress);
...
case ContextProvider:
...
popProvider(context, workInProgress);
...
case OffscreenComponent:
case LegacyHiddenComponent: {
popSuspenseHandler(workInProgress);
popHiddenContext(workInProgress);
popTransition(workInProgress, current);
...
}
case CacheComponent:
...
popCacheProvider(workInProgress, cache);
...
default:
return null;
}
}
unwindWork
은 작업에 에러가 생겼을 때 호출되므로 Fiber Stack에서 pop
한 후 이전 상태로 복구합니다.
unwindInterruptedWork
react/packages/react-reconciler/src/ReactFiberUnwindWork.js
function unwindInterruptedWork(
current: Fiber | null,
interruptedWork: Fiber,
renderLanes: Lanes,
) {
...
switch (interruptedWork.tag) {
case HostRoot: {
popCacheProvider(interruptedWork, cache);
popRootMarkerInstance(interruptedWork);
popRootTransition(interruptedWork, root, renderLanes);
popHostContainer(interruptedWork);
popTopLevelLegacyContextObject(interruptedWork);
...
}
case HostHoistable:
case HostSingleton:
case HostComponent: {
popHostContext(interruptedWork);
...
}
case HostPortal:
popHostContainer(interruptedWork);
...
case SuspenseComponent:
popSuspenseHandler(interruptedWork);
...
case SuspenseListComponent:
popSuspenseListContext(interruptedWork);
...
case ContextProvider:
...
popProvider(context, interruptedWork);
...
case OffscreenComponent:
case LegacyHiddenComponent:
popSuspenseHandler(interruptedWork);
popHiddenContext(interruptedWork);
popTransition(interruptedWork, current);
...
case CacheComponent:
...
popCacheProvider(interruptedWork, cache);
...
break;
default:
break;
}
}
unwindInterruptedWork
은 Suspense
혹은 우선순위 변동으로 작업에 중단이 생겼을 때 호출되므로 Fiber Stack에서 pop
한 후 이전 상태로 복구합니다.