BlogReactReconcilerFiber Stack

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;
  }
}

unwindInterruptedWorkSuspense 혹은 우선순위 변동으로 작업에 중단이 생겼을 때 호출되므로 Fiber Stack에서 pop한 후 이전 상태로 복구합니다.