Land rest of effects refactor in main fork (#20644)
This commit is contained in:
parent
a6b5256a29
commit
0fd6805c6d
|
@ -116,7 +116,7 @@ module.exports = {
|
||||||
'react-internal/no-cross-fork-types': [
|
'react-internal/no-cross-fork-types': [
|
||||||
ERROR,
|
ERROR,
|
||||||
{
|
{
|
||||||
old: ['firstEffect', 'nextEffect'],
|
old: [],
|
||||||
new: [],
|
new: [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -13,12 +13,7 @@ import type {Fiber} from './ReactInternalTypes';
|
||||||
import type {Lanes} from './ReactFiberLane.old';
|
import type {Lanes} from './ReactFiberLane.old';
|
||||||
|
|
||||||
import getComponentName from 'shared/getComponentName';
|
import getComponentName from 'shared/getComponentName';
|
||||||
import {
|
import {Placement, ChildDeletion} from './ReactFiberFlags';
|
||||||
Deletion,
|
|
||||||
ChildDeletion,
|
|
||||||
Placement,
|
|
||||||
StaticMask,
|
|
||||||
} from './ReactFiberFlags';
|
|
||||||
import {
|
import {
|
||||||
getIteratorFn,
|
getIteratorFn,
|
||||||
REACT_ELEMENT_TYPE,
|
REACT_ELEMENT_TYPE,
|
||||||
|
@ -268,21 +263,6 @@ function ChildReconciler(shouldTrackSideEffects) {
|
||||||
// Noop.
|
// Noop.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Deletions are added in reversed order so we add it to the front.
|
|
||||||
// At this point, the return fiber's effect list is empty except for
|
|
||||||
// deletions, so we can just append the deletion to the list. The remaining
|
|
||||||
// effects aren't added until the complete phase. Once we implement
|
|
||||||
// resuming, this may not be true.
|
|
||||||
const last = returnFiber.lastEffect;
|
|
||||||
if (last !== null) {
|
|
||||||
last.nextEffect = childToDelete;
|
|
||||||
returnFiber.lastEffect = childToDelete;
|
|
||||||
} else {
|
|
||||||
returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
|
|
||||||
}
|
|
||||||
childToDelete.nextEffect = null;
|
|
||||||
childToDelete.flags = (childToDelete.flags & StaticMask) | Deletion;
|
|
||||||
|
|
||||||
const deletions = returnFiber.deletions;
|
const deletions = returnFiber.deletions;
|
||||||
if (deletions === null) {
|
if (deletions === null) {
|
||||||
returnFiber.deletions = [childToDelete];
|
returnFiber.deletions = [childToDelete];
|
||||||
|
|
|
@ -144,10 +144,6 @@ function FiberNode(
|
||||||
|
|
||||||
// Effects
|
// Effects
|
||||||
this.flags = NoFlags;
|
this.flags = NoFlags;
|
||||||
this.nextEffect = null;
|
|
||||||
|
|
||||||
this.firstEffect = null;
|
|
||||||
this.lastEffect = null;
|
|
||||||
this.subtreeFlags = NoFlags;
|
this.subtreeFlags = NoFlags;
|
||||||
this.deletions = null;
|
this.deletions = null;
|
||||||
|
|
||||||
|
@ -285,10 +281,7 @@ export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
|
||||||
// Reset the effect tag.
|
// Reset the effect tag.
|
||||||
workInProgress.flags = NoFlags;
|
workInProgress.flags = NoFlags;
|
||||||
|
|
||||||
// The effect list is no longer valid.
|
// The effects are no longer valid.
|
||||||
workInProgress.nextEffect = null;
|
|
||||||
workInProgress.firstEffect = null;
|
|
||||||
workInProgress.lastEffect = null;
|
|
||||||
workInProgress.subtreeFlags = NoFlags;
|
workInProgress.subtreeFlags = NoFlags;
|
||||||
workInProgress.deletions = null;
|
workInProgress.deletions = null;
|
||||||
|
|
||||||
|
@ -370,10 +363,7 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
|
||||||
// that child fiber is setting, not the reconciliation.
|
// that child fiber is setting, not the reconciliation.
|
||||||
workInProgress.flags &= StaticMask | Placement;
|
workInProgress.flags &= StaticMask | Placement;
|
||||||
|
|
||||||
// The effect list is no longer valid.
|
// The effects are no longer valid.
|
||||||
workInProgress.nextEffect = null;
|
|
||||||
workInProgress.firstEffect = null;
|
|
||||||
workInProgress.lastEffect = null;
|
|
||||||
|
|
||||||
const current = workInProgress.alternate;
|
const current = workInProgress.alternate;
|
||||||
if (current === null) {
|
if (current === null) {
|
||||||
|
@ -403,6 +393,9 @@ export function resetWorkInProgress(workInProgress: Fiber, renderLanes: Lanes) {
|
||||||
workInProgress.lanes = current.lanes;
|
workInProgress.lanes = current.lanes;
|
||||||
|
|
||||||
workInProgress.child = current.child;
|
workInProgress.child = current.child;
|
||||||
|
// TODO: `subtreeFlags` should be reset to NoFlags, like we do in
|
||||||
|
// `createWorkInProgress`. Nothing reads this until the complete phase,
|
||||||
|
// currently, but it might in the future, and we should be consistent.
|
||||||
workInProgress.subtreeFlags = current.subtreeFlags;
|
workInProgress.subtreeFlags = current.subtreeFlags;
|
||||||
workInProgress.deletions = null;
|
workInProgress.deletions = null;
|
||||||
workInProgress.memoizedProps = current.memoizedProps;
|
workInProgress.memoizedProps = current.memoizedProps;
|
||||||
|
@ -847,9 +840,6 @@ export function assignFiberPropertiesInDEV(
|
||||||
target.dependencies = source.dependencies;
|
target.dependencies = source.dependencies;
|
||||||
target.mode = source.mode;
|
target.mode = source.mode;
|
||||||
target.flags = source.flags;
|
target.flags = source.flags;
|
||||||
target.nextEffect = source.nextEffect;
|
|
||||||
target.firstEffect = source.firstEffect;
|
|
||||||
target.lastEffect = source.lastEffect;
|
|
||||||
target.subtreeFlags = source.subtreeFlags;
|
target.subtreeFlags = source.subtreeFlags;
|
||||||
target.deletions = source.deletions;
|
target.deletions = source.deletions;
|
||||||
target.lanes = source.lanes;
|
target.lanes = source.lanes;
|
||||||
|
|
|
@ -67,7 +67,6 @@ import {
|
||||||
DidCapture,
|
DidCapture,
|
||||||
Update,
|
Update,
|
||||||
Ref,
|
Ref,
|
||||||
Deletion,
|
|
||||||
ChildDeletion,
|
ChildDeletion,
|
||||||
ForceUpdateForLegacySuspense,
|
ForceUpdateForLegacySuspense,
|
||||||
StaticMask,
|
StaticMask,
|
||||||
|
@ -2199,10 +2198,6 @@ function updateSuspensePrimaryChildren(
|
||||||
primaryChildFragment.sibling = null;
|
primaryChildFragment.sibling = null;
|
||||||
if (currentFallbackChildFragment !== null) {
|
if (currentFallbackChildFragment !== null) {
|
||||||
// Delete the fallback child fragment
|
// Delete the fallback child fragment
|
||||||
currentFallbackChildFragment.nextEffect = null;
|
|
||||||
currentFallbackChildFragment.flags =
|
|
||||||
(currentFallbackChildFragment.flags & StaticMask) | Deletion;
|
|
||||||
workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChildFragment;
|
|
||||||
const deletions = workInProgress.deletions;
|
const deletions = workInProgress.deletions;
|
||||||
if (deletions === null) {
|
if (deletions === null) {
|
||||||
workInProgress.deletions = [currentFallbackChildFragment];
|
workInProgress.deletions = [currentFallbackChildFragment];
|
||||||
|
@ -2264,22 +2259,9 @@ function updateSuspenseFallbackChildren(
|
||||||
currentPrimaryChildFragment.treeBaseDuration;
|
currentPrimaryChildFragment.treeBaseDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentFallbackChildFragment !== null) {
|
// The fallback fiber was added as a deletion during the first pass.
|
||||||
// The fallback fiber was added as a deletion effect during the first
|
// However, since we're going to remain on the fallback, we no longer want
|
||||||
// pass. However, since we're going to remain on the fallback, we no
|
// to delete it.
|
||||||
// longer want to delete it. So we need to remove it from the list.
|
|
||||||
// Deletions are stored on the same list as effects, and are always added
|
|
||||||
// to the front. So we know that the first effect must be the fallback
|
|
||||||
// deletion effect, and everything after that is from the primary free.
|
|
||||||
const firstPrimaryTreeEffect = currentFallbackChildFragment.nextEffect;
|
|
||||||
if (firstPrimaryTreeEffect !== null) {
|
|
||||||
workInProgress.firstEffect = firstPrimaryTreeEffect;
|
|
||||||
} else {
|
|
||||||
// TODO: Reset this somewhere else? Lol legacy mode is so weird.
|
|
||||||
workInProgress.firstEffect = workInProgress.lastEffect = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
workInProgress.deletions = null;
|
workInProgress.deletions = null;
|
||||||
} else {
|
} else {
|
||||||
primaryChildFragment = createWorkInProgressOffscreenFiber(
|
primaryChildFragment = createWorkInProgressOffscreenFiber(
|
||||||
|
@ -2776,7 +2758,6 @@ function initSuspenseListRenderState(
|
||||||
tail: null | Fiber,
|
tail: null | Fiber,
|
||||||
lastContentRow: null | Fiber,
|
lastContentRow: null | Fiber,
|
||||||
tailMode: SuspenseListTailMode,
|
tailMode: SuspenseListTailMode,
|
||||||
lastEffectBeforeRendering: null | Fiber,
|
|
||||||
): void {
|
): void {
|
||||||
const renderState: null | SuspenseListRenderState =
|
const renderState: null | SuspenseListRenderState =
|
||||||
workInProgress.memoizedState;
|
workInProgress.memoizedState;
|
||||||
|
@ -2788,7 +2769,6 @@ function initSuspenseListRenderState(
|
||||||
last: lastContentRow,
|
last: lastContentRow,
|
||||||
tail: tail,
|
tail: tail,
|
||||||
tailMode: tailMode,
|
tailMode: tailMode,
|
||||||
lastEffect: lastEffectBeforeRendering,
|
|
||||||
}: SuspenseListRenderState);
|
}: SuspenseListRenderState);
|
||||||
} else {
|
} else {
|
||||||
// We can reuse the existing object from previous renders.
|
// We can reuse the existing object from previous renders.
|
||||||
|
@ -2798,7 +2778,6 @@ function initSuspenseListRenderState(
|
||||||
renderState.last = lastContentRow;
|
renderState.last = lastContentRow;
|
||||||
renderState.tail = tail;
|
renderState.tail = tail;
|
||||||
renderState.tailMode = tailMode;
|
renderState.tailMode = tailMode;
|
||||||
renderState.lastEffect = lastEffectBeforeRendering;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2880,7 +2859,6 @@ function updateSuspenseListComponent(
|
||||||
tail,
|
tail,
|
||||||
lastContentRow,
|
lastContentRow,
|
||||||
tailMode,
|
tailMode,
|
||||||
workInProgress.lastEffect,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2912,7 +2890,6 @@ function updateSuspenseListComponent(
|
||||||
tail,
|
tail,
|
||||||
null, // last
|
null, // last
|
||||||
tailMode,
|
tailMode,
|
||||||
workInProgress.lastEffect,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2923,7 +2900,6 @@ function updateSuspenseListComponent(
|
||||||
null, // tail
|
null, // tail
|
||||||
null, // last
|
null, // last
|
||||||
undefined,
|
undefined,
|
||||||
workInProgress.lastEffect,
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3183,16 +3159,6 @@ function remountFiber(
|
||||||
|
|
||||||
// Delete the old fiber and place the new one.
|
// Delete the old fiber and place the new one.
|
||||||
// Since the old fiber is disconnected, we have to schedule it manually.
|
// Since the old fiber is disconnected, we have to schedule it manually.
|
||||||
const last = returnFiber.lastEffect;
|
|
||||||
if (last !== null) {
|
|
||||||
last.nextEffect = current;
|
|
||||||
returnFiber.lastEffect = current;
|
|
||||||
} else {
|
|
||||||
returnFiber.firstEffect = returnFiber.lastEffect = current;
|
|
||||||
}
|
|
||||||
current.nextEffect = null;
|
|
||||||
current.flags = (current.flags & StaticMask) | Deletion;
|
|
||||||
|
|
||||||
const deletions = returnFiber.deletions;
|
const deletions = returnFiber.deletions;
|
||||||
if (deletions === null) {
|
if (deletions === null) {
|
||||||
returnFiber.deletions = [current];
|
returnFiber.deletions = [current];
|
||||||
|
|
|
@ -76,9 +76,10 @@ import {
|
||||||
Hydrating,
|
Hydrating,
|
||||||
HydratingAndUpdate,
|
HydratingAndUpdate,
|
||||||
Passive,
|
Passive,
|
||||||
|
BeforeMutationMask,
|
||||||
MutationMask,
|
MutationMask,
|
||||||
PassiveMask,
|
|
||||||
LayoutMask,
|
LayoutMask,
|
||||||
|
PassiveMask,
|
||||||
PassiveUnmountPendingDev,
|
PassiveUnmountPendingDev,
|
||||||
} from './ReactFiberFlags';
|
} from './ReactFiberFlags';
|
||||||
import getComponentName from 'shared/getComponentName';
|
import getComponentName from 'shared/getComponentName';
|
||||||
|
@ -129,13 +130,13 @@ import {
|
||||||
commitHydratedSuspenseInstance,
|
commitHydratedSuspenseInstance,
|
||||||
clearContainer,
|
clearContainer,
|
||||||
prepareScopeUpdate,
|
prepareScopeUpdate,
|
||||||
|
prepareForCommit,
|
||||||
|
beforeActiveInstanceBlur,
|
||||||
} from './ReactFiberHostConfig';
|
} from './ReactFiberHostConfig';
|
||||||
import {
|
import {
|
||||||
captureCommitPhaseError,
|
captureCommitPhaseError,
|
||||||
resolveRetryWakeable,
|
resolveRetryWakeable,
|
||||||
markCommitTimeOfFallback,
|
markCommitTimeOfFallback,
|
||||||
enqueuePendingPassiveHookEffectMount,
|
|
||||||
enqueuePendingPassiveHookEffectUnmount,
|
|
||||||
enqueuePendingPassiveProfilerEffect,
|
enqueuePendingPassiveProfilerEffect,
|
||||||
} from './ReactFiberWorkLoop.old';
|
} from './ReactFiberWorkLoop.old';
|
||||||
import {
|
import {
|
||||||
|
@ -145,6 +146,7 @@ import {
|
||||||
Passive as HookPassive,
|
Passive as HookPassive,
|
||||||
} from './ReactHookEffectTags';
|
} from './ReactHookEffectTags';
|
||||||
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.old';
|
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.old';
|
||||||
|
import {doesFiberContain} from './ReactFiberTreeReflection';
|
||||||
|
|
||||||
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
|
let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
@ -260,18 +262,114 @@ function safelyCallDestroy(current: Fiber, destroy: () => void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function commitBeforeMutationLifeCycles(
|
let focusedInstanceHandle: null | Fiber = null;
|
||||||
current: Fiber | null,
|
let shouldFireAfterActiveInstanceBlur: boolean = false;
|
||||||
finishedWork: Fiber,
|
|
||||||
): void {
|
export function commitBeforeMutationEffects(
|
||||||
switch (finishedWork.tag) {
|
root: FiberRoot,
|
||||||
case FunctionComponent:
|
firstChild: Fiber,
|
||||||
case ForwardRef:
|
) {
|
||||||
case SimpleMemoComponent: {
|
focusedInstanceHandle = prepareForCommit(root.containerInfo);
|
||||||
|
|
||||||
|
nextEffect = firstChild;
|
||||||
|
commitBeforeMutationEffects_begin();
|
||||||
|
|
||||||
|
// We no longer need to track the active instance fiber
|
||||||
|
const shouldFire = shouldFireAfterActiveInstanceBlur;
|
||||||
|
shouldFireAfterActiveInstanceBlur = false;
|
||||||
|
focusedInstanceHandle = null;
|
||||||
|
|
||||||
|
return shouldFire;
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitBeforeMutationEffects_begin() {
|
||||||
|
while (nextEffect !== null) {
|
||||||
|
const fiber = nextEffect;
|
||||||
|
|
||||||
|
// TODO: Should wrap this in flags check, too, as optimization
|
||||||
|
const deletions = fiber.deletions;
|
||||||
|
if (deletions !== null) {
|
||||||
|
for (let i = 0; i < deletions.length; i++) {
|
||||||
|
const deletion = deletions[i];
|
||||||
|
commitBeforeMutationEffectsDeletion(deletion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const child = fiber.child;
|
||||||
|
if (
|
||||||
|
(fiber.subtreeFlags & BeforeMutationMask) !== NoFlags &&
|
||||||
|
child !== null
|
||||||
|
) {
|
||||||
|
ensureCorrectReturnPointer(child, fiber);
|
||||||
|
nextEffect = child;
|
||||||
|
} else {
|
||||||
|
commitBeforeMutationEffects_complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitBeforeMutationEffects_complete() {
|
||||||
|
while (nextEffect !== null) {
|
||||||
|
const fiber = nextEffect;
|
||||||
|
if (__DEV__) {
|
||||||
|
setCurrentDebugFiberInDEV(fiber);
|
||||||
|
invokeGuardedCallback(
|
||||||
|
null,
|
||||||
|
commitBeforeMutationEffectsOnFiber,
|
||||||
|
null,
|
||||||
|
fiber,
|
||||||
|
);
|
||||||
|
if (hasCaughtError()) {
|
||||||
|
const error = clearCaughtError();
|
||||||
|
captureCommitPhaseError(fiber, error);
|
||||||
|
}
|
||||||
|
resetCurrentDebugFiberInDEV();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
commitBeforeMutationEffectsOnFiber(fiber);
|
||||||
|
} catch (error) {
|
||||||
|
captureCommitPhaseError(fiber, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sibling = fiber.sibling;
|
||||||
|
if (sibling !== null) {
|
||||||
|
ensureCorrectReturnPointer(sibling, fiber.return);
|
||||||
|
nextEffect = sibling;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case ClassComponent: {
|
|
||||||
if (finishedWork.flags & Snapshot) {
|
nextEffect = fiber.return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
|
||||||
|
const current = finishedWork.alternate;
|
||||||
|
const flags = finishedWork.flags;
|
||||||
|
|
||||||
|
if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
|
||||||
|
// Check to see if the focused element was inside of a hidden (Suspense) subtree.
|
||||||
|
// TODO: Move this out of the hot path using a dedicated effect tag.
|
||||||
|
if (
|
||||||
|
finishedWork.tag === SuspenseComponent &&
|
||||||
|
isSuspenseBoundaryBeingHidden(current, finishedWork) &&
|
||||||
|
doesFiberContain(finishedWork, focusedInstanceHandle)
|
||||||
|
) {
|
||||||
|
shouldFireAfterActiveInstanceBlur = true;
|
||||||
|
beforeActiveInstanceBlur(finishedWork);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & Snapshot) !== NoFlags) {
|
||||||
|
setCurrentDebugFiberInDEV(finishedWork);
|
||||||
|
|
||||||
|
switch (finishedWork.tag) {
|
||||||
|
case FunctionComponent:
|
||||||
|
case ForwardRef:
|
||||||
|
case SimpleMemoComponent: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ClassComponent: {
|
||||||
if (current !== null) {
|
if (current !== null) {
|
||||||
const prevProps = current.memoizedProps;
|
const prevProps = current.memoizedProps;
|
||||||
const prevState = current.memoizedState;
|
const prevState = current.memoizedState;
|
||||||
|
@ -325,30 +423,43 @@ function commitBeforeMutationLifeCycles(
|
||||||
}
|
}
|
||||||
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
|
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return;
|
case HostRoot: {
|
||||||
}
|
if (supportsMutation) {
|
||||||
case HostRoot: {
|
|
||||||
if (supportsMutation) {
|
|
||||||
if (finishedWork.flags & Snapshot) {
|
|
||||||
const root = finishedWork.stateNode;
|
const root = finishedWork.stateNode;
|
||||||
clearContainer(root.containerInfo);
|
clearContainer(root.containerInfo);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HostComponent:
|
||||||
|
case HostText:
|
||||||
|
case HostPortal:
|
||||||
|
case IncompleteClassComponent:
|
||||||
|
// Nothing to do for these component types
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
invariant(
|
||||||
|
false,
|
||||||
|
'This unit of work tag should not have side-effects. This error is ' +
|
||||||
|
'likely caused by a bug in React. Please file an issue.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
case HostComponent:
|
|
||||||
case HostText:
|
resetCurrentDebugFiberInDEV();
|
||||||
case HostPortal:
|
}
|
||||||
case IncompleteClassComponent:
|
}
|
||||||
// Nothing to do for these component types
|
|
||||||
return;
|
function commitBeforeMutationEffectsDeletion(deletion: Fiber) {
|
||||||
|
// TODO (effects) It would be nice to avoid calling doesFiberContain()
|
||||||
|
// Maybe we can repurpose one of the subtreeFlags positions for this instead?
|
||||||
|
// Use it to store which part of the tree the focused instance is in?
|
||||||
|
// This assumes we can safely determine that instance during the "render" phase.
|
||||||
|
if (doesFiberContain(deletion, ((focusedInstanceHandle: any): Fiber))) {
|
||||||
|
shouldFireAfterActiveInstanceBlur = true;
|
||||||
|
beforeActiveInstanceBlur(deletion);
|
||||||
}
|
}
|
||||||
invariant(
|
|
||||||
false,
|
|
||||||
'This unit of work tag should not have side-effects. This error is ' +
|
|
||||||
'likely caused by a bug in React. Please file an issue.',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function commitHookEffectListUnmount(flags: HookFlags, finishedWork: Fiber) {
|
function commitHookEffectListUnmount(flags: HookFlags, finishedWork: Fiber) {
|
||||||
|
@ -421,26 +532,6 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function schedulePassiveEffects(finishedWork: Fiber) {
|
|
||||||
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
|
|
||||||
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
|
|
||||||
if (lastEffect !== null) {
|
|
||||||
const firstEffect = lastEffect.next;
|
|
||||||
let effect = firstEffect;
|
|
||||||
do {
|
|
||||||
const {next, tag} = effect;
|
|
||||||
if (
|
|
||||||
(tag & HookPassive) !== NoHookEffect &&
|
|
||||||
(tag & HookHasEffect) !== NoHookEffect
|
|
||||||
) {
|
|
||||||
enqueuePendingPassiveHookEffectUnmount(finishedWork, effect);
|
|
||||||
enqueuePendingPassiveHookEffectMount(finishedWork, effect);
|
|
||||||
}
|
|
||||||
effect = next;
|
|
||||||
} while (effect !== firstEffect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function commitPassiveEffectDurations(
|
export function commitPassiveEffectDurations(
|
||||||
finishedRoot: FiberRoot,
|
finishedRoot: FiberRoot,
|
||||||
finishedWork: Fiber,
|
finishedWork: Fiber,
|
||||||
|
@ -527,8 +618,6 @@ function commitLayoutEffectOnFiber(
|
||||||
} else {
|
} else {
|
||||||
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
|
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
|
||||||
}
|
}
|
||||||
|
|
||||||
schedulePassiveEffects(finishedWork);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ClassComponent: {
|
case ClassComponent: {
|
||||||
|
@ -979,9 +1068,7 @@ function commitUnmount(
|
||||||
do {
|
do {
|
||||||
const {destroy, tag} = effect;
|
const {destroy, tag} = effect;
|
||||||
if (destroy !== undefined) {
|
if (destroy !== undefined) {
|
||||||
if ((tag & HookPassive) !== NoHookEffect) {
|
if ((tag & HookLayout) !== NoHookEffect) {
|
||||||
enqueuePendingPassiveHookEffectUnmount(current, effect);
|
|
||||||
} else {
|
|
||||||
if (
|
if (
|
||||||
enableProfilerTimer &&
|
enableProfilerTimer &&
|
||||||
enableProfilerCommitHooks &&
|
enableProfilerCommitHooks &&
|
||||||
|
@ -1127,9 +1214,6 @@ export function detachFiberAfterEffects(fiber: Fiber): void {
|
||||||
fiber.sibling = null;
|
fiber.sibling = null;
|
||||||
fiber.stateNode = null;
|
fiber.stateNode = null;
|
||||||
fiber.updateQueue = null;
|
fiber.updateQueue = null;
|
||||||
fiber.nextEffect = null;
|
|
||||||
fiber.firstEffect = null;
|
|
||||||
fiber.lastEffect = null;
|
|
||||||
|
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
fiber._debugOwner = null;
|
fiber._debugOwner = null;
|
||||||
|
@ -2483,7 +2567,6 @@ function invokePassiveEffectUnmountInDEV(fiber: Fiber): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
commitBeforeMutationLifeCycles,
|
|
||||||
commitResetTextContent,
|
commitResetTextContent,
|
||||||
commitPlacement,
|
commitPlacement,
|
||||||
commitDeletion,
|
commitDeletion,
|
||||||
|
|
|
@ -72,7 +72,9 @@ import {
|
||||||
NoFlags,
|
NoFlags,
|
||||||
DidCapture,
|
DidCapture,
|
||||||
Snapshot,
|
Snapshot,
|
||||||
|
ChildDeletion,
|
||||||
StaticMask,
|
StaticMask,
|
||||||
|
MutationMask,
|
||||||
} from './ReactFiberFlags';
|
} from './ReactFiberFlags';
|
||||||
import invariant from 'shared/invariant';
|
import invariant from 'shared/invariant';
|
||||||
|
|
||||||
|
@ -173,6 +175,31 @@ function markRef(workInProgress: Fiber) {
|
||||||
workInProgress.flags |= Ref;
|
workInProgress.flags |= Ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hadNoMutationsEffects(current: null | Fiber, completedWork: Fiber) {
|
||||||
|
const didBailout = current !== null && current.child === completedWork.child;
|
||||||
|
if (didBailout) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((completedWork.flags & ChildDeletion) !== NoFlags) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties`
|
||||||
|
// then we only have to check the `completedWork.subtreeFlags`.
|
||||||
|
let child = completedWork.child;
|
||||||
|
while (child !== null) {
|
||||||
|
if (
|
||||||
|
(child.flags & MutationMask) !== NoFlags ||
|
||||||
|
(child.subtreeFlags & MutationMask) !== NoFlags
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
child = child.sibling;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
let appendAllChildren;
|
let appendAllChildren;
|
||||||
let updateHostContainer;
|
let updateHostContainer;
|
||||||
let updateHostComponent;
|
let updateHostComponent;
|
||||||
|
@ -217,7 +244,7 @@ if (supportsMutation) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
updateHostContainer = function(workInProgress: Fiber) {
|
updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {
|
||||||
// Noop
|
// Noop
|
||||||
};
|
};
|
||||||
updateHostComponent = function(
|
updateHostComponent = function(
|
||||||
|
@ -461,13 +488,13 @@ if (supportsMutation) {
|
||||||
node = node.sibling;
|
node = node.sibling;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
updateHostContainer = function(workInProgress: Fiber) {
|
updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {
|
||||||
const portalOrRoot: {
|
const portalOrRoot: {
|
||||||
containerInfo: Container,
|
containerInfo: Container,
|
||||||
pendingChildren: ChildSet,
|
pendingChildren: ChildSet,
|
||||||
...
|
...
|
||||||
} = workInProgress.stateNode;
|
} = workInProgress.stateNode;
|
||||||
const childrenUnchanged = workInProgress.firstEffect === null;
|
const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);
|
||||||
if (childrenUnchanged) {
|
if (childrenUnchanged) {
|
||||||
// No changes, just reuse the existing instance.
|
// No changes, just reuse the existing instance.
|
||||||
} else {
|
} else {
|
||||||
|
@ -492,7 +519,7 @@ if (supportsMutation) {
|
||||||
const oldProps = current.memoizedProps;
|
const oldProps = current.memoizedProps;
|
||||||
// If there are no effects associated with this node, then none of our children had any updates.
|
// If there are no effects associated with this node, then none of our children had any updates.
|
||||||
// This guarantees that we can reuse all of them.
|
// This guarantees that we can reuse all of them.
|
||||||
const childrenUnchanged = workInProgress.firstEffect === null;
|
const childrenUnchanged = hadNoMutationsEffects(current, workInProgress);
|
||||||
if (childrenUnchanged && oldProps === newProps) {
|
if (childrenUnchanged && oldProps === newProps) {
|
||||||
// No changes, just reuse the existing instance.
|
// No changes, just reuse the existing instance.
|
||||||
// Note that this might release a previous clone.
|
// Note that this might release a previous clone.
|
||||||
|
@ -575,7 +602,7 @@ if (supportsMutation) {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// No host operations
|
// No host operations
|
||||||
updateHostContainer = function(workInProgress: Fiber) {
|
updateHostContainer = function(current: null | Fiber, workInProgress: Fiber) {
|
||||||
// Noop
|
// Noop
|
||||||
};
|
};
|
||||||
updateHostComponent = function(
|
updateHostComponent = function(
|
||||||
|
@ -847,7 +874,7 @@ function completeWork(
|
||||||
workInProgress.flags |= Snapshot;
|
workInProgress.flags |= Snapshot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateHostContainer(workInProgress);
|
updateHostContainer(current, workInProgress);
|
||||||
bubbleProperties(workInProgress);
|
bubbleProperties(workInProgress);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1142,7 +1169,7 @@ function completeWork(
|
||||||
}
|
}
|
||||||
case HostPortal:
|
case HostPortal:
|
||||||
popHostContainer(workInProgress);
|
popHostContainer(workInProgress);
|
||||||
updateHostContainer(workInProgress);
|
updateHostContainer(current, workInProgress);
|
||||||
if (current === null) {
|
if (current === null) {
|
||||||
preparePortalMount(workInProgress.stateNode.containerInfo);
|
preparePortalMount(workInProgress.stateNode.containerInfo);
|
||||||
}
|
}
|
||||||
|
@ -1226,11 +1253,7 @@ function completeWork(
|
||||||
|
|
||||||
// Rerender the whole list, but this time, we'll force fallbacks
|
// Rerender the whole list, but this time, we'll force fallbacks
|
||||||
// to stay in place.
|
// to stay in place.
|
||||||
// Reset the effect list before doing the second pass since that's now invalid.
|
// Reset the effect flags before doing the second pass since that's now invalid.
|
||||||
if (renderState.lastEffect === null) {
|
|
||||||
workInProgress.firstEffect = null;
|
|
||||||
}
|
|
||||||
workInProgress.lastEffect = renderState.lastEffect;
|
|
||||||
// Reset the child fibers to their original state.
|
// Reset the child fibers to their original state.
|
||||||
workInProgress.subtreeFlags = NoFlags;
|
workInProgress.subtreeFlags = NoFlags;
|
||||||
resetChildFibers(workInProgress, renderLanes);
|
resetChildFibers(workInProgress, renderLanes);
|
||||||
|
@ -1301,15 +1324,6 @@ function completeWork(
|
||||||
!renderedTail.alternate &&
|
!renderedTail.alternate &&
|
||||||
!getIsHydrating() // We don't cut it if we're hydrating.
|
!getIsHydrating() // We don't cut it if we're hydrating.
|
||||||
) {
|
) {
|
||||||
// We need to delete the row we just rendered.
|
|
||||||
// Reset the effect list to what it was before we rendered this
|
|
||||||
// child. The nested children have already appended themselves.
|
|
||||||
const lastEffect = (workInProgress.lastEffect =
|
|
||||||
renderState.lastEffect);
|
|
||||||
// Remove any effects that were appended after this point.
|
|
||||||
if (lastEffect !== null) {
|
|
||||||
lastEffect.nextEffect = null;
|
|
||||||
}
|
|
||||||
// We're done.
|
// We're done.
|
||||||
bubbleProperties(workInProgress);
|
bubbleProperties(workInProgress);
|
||||||
return null;
|
return null;
|
||||||
|
@ -1369,7 +1383,6 @@ function completeWork(
|
||||||
const next = renderState.tail;
|
const next = renderState.tail;
|
||||||
renderState.rendering = next;
|
renderState.rendering = next;
|
||||||
renderState.tail = next.sibling;
|
renderState.tail = next.sibling;
|
||||||
renderState.lastEffect = workInProgress.lastEffect;
|
|
||||||
renderState.renderingStartTime = now();
|
renderState.renderingStartTime = now();
|
||||||
next.sibling = null;
|
next.sibling = null;
|
||||||
|
|
||||||
|
|
|
@ -488,8 +488,8 @@ export function bailoutHooks(
|
||||||
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
|
(workInProgress.mode & (BlockingMode | ConcurrentMode)) !== NoMode
|
||||||
) {
|
) {
|
||||||
workInProgress.flags &= ~(
|
workInProgress.flags &= ~(
|
||||||
MountLayoutDevEffect |
|
|
||||||
MountPassiveDevEffect |
|
MountPassiveDevEffect |
|
||||||
|
MountLayoutDevEffect |
|
||||||
PassiveEffect |
|
PassiveEffect |
|
||||||
UpdateEffect
|
UpdateEffect
|
||||||
);
|
);
|
||||||
|
@ -1337,17 +1337,14 @@ function mountEffect(
|
||||||
(currentlyRenderingFiber.mode & (BlockingMode | ConcurrentMode)) !== NoMode
|
(currentlyRenderingFiber.mode & (BlockingMode | ConcurrentMode)) !== NoMode
|
||||||
) {
|
) {
|
||||||
return mountEffectImpl(
|
return mountEffectImpl(
|
||||||
MountPassiveDevEffect |
|
MountPassiveDevEffect | PassiveEffect | PassiveStaticEffect,
|
||||||
UpdateEffect |
|
|
||||||
PassiveEffect |
|
|
||||||
PassiveStaticEffect,
|
|
||||||
HookPassive,
|
HookPassive,
|
||||||
create,
|
create,
|
||||||
deps,
|
deps,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return mountEffectImpl(
|
return mountEffectImpl(
|
||||||
UpdateEffect | PassiveEffect | PassiveStaticEffect,
|
PassiveEffect | PassiveStaticEffect,
|
||||||
HookPassive,
|
HookPassive,
|
||||||
create,
|
create,
|
||||||
deps,
|
deps,
|
||||||
|
@ -1365,12 +1362,7 @@ function updateEffect(
|
||||||
warnIfNotCurrentlyActingEffectsInDEV(currentlyRenderingFiber);
|
warnIfNotCurrentlyActingEffectsInDEV(currentlyRenderingFiber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updateEffectImpl(
|
return updateEffectImpl(PassiveEffect, HookPassive, create, deps);
|
||||||
UpdateEffect | PassiveEffect,
|
|
||||||
HookPassive,
|
|
||||||
create,
|
|
||||||
deps,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mountLayoutEffect(
|
function mountLayoutEffect(
|
||||||
|
@ -1749,10 +1741,9 @@ function mountOpaqueIdentifier(): OpaqueIDType | void {
|
||||||
|
|
||||||
if ((currentlyRenderingFiber.mode & BlockingMode) === NoMode) {
|
if ((currentlyRenderingFiber.mode & BlockingMode) === NoMode) {
|
||||||
if (__DEV__ && enableDoubleInvokingEffects) {
|
if (__DEV__ && enableDoubleInvokingEffects) {
|
||||||
currentlyRenderingFiber.flags |=
|
currentlyRenderingFiber.flags |= MountPassiveDevEffect | PassiveEffect;
|
||||||
MountPassiveDevEffect | UpdateEffect | PassiveEffect;
|
|
||||||
} else {
|
} else {
|
||||||
currentlyRenderingFiber.flags |= UpdateEffect | PassiveEffect;
|
currentlyRenderingFiber.flags |= PassiveEffect;
|
||||||
}
|
}
|
||||||
pushEffect(
|
pushEffect(
|
||||||
HookHasEffect | HookPassive,
|
HookHasEffect | HookPassive,
|
||||||
|
|
|
@ -24,13 +24,7 @@ import {
|
||||||
HostRoot,
|
HostRoot,
|
||||||
SuspenseComponent,
|
SuspenseComponent,
|
||||||
} from './ReactWorkTags';
|
} from './ReactWorkTags';
|
||||||
import {
|
import {ChildDeletion, Placement, Hydrating} from './ReactFiberFlags';
|
||||||
Deletion,
|
|
||||||
ChildDeletion,
|
|
||||||
Placement,
|
|
||||||
Hydrating,
|
|
||||||
StaticMask,
|
|
||||||
} from './ReactFiberFlags';
|
|
||||||
import invariant from 'shared/invariant';
|
import invariant from 'shared/invariant';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -130,19 +124,6 @@ function deleteHydratableInstance(
|
||||||
const childToDelete = createFiberFromHostInstanceForDeletion();
|
const childToDelete = createFiberFromHostInstanceForDeletion();
|
||||||
childToDelete.stateNode = instance;
|
childToDelete.stateNode = instance;
|
||||||
childToDelete.return = returnFiber;
|
childToDelete.return = returnFiber;
|
||||||
childToDelete.flags = (childToDelete.flags & StaticMask) | Deletion;
|
|
||||||
|
|
||||||
// This might seem like it belongs on progressedFirstDeletion. However,
|
|
||||||
// these children are not part of the reconciliation list of children.
|
|
||||||
// Even if we abort and rereconcile the children, that will try to hydrate
|
|
||||||
// again and the nodes are still in the host tree so these will be
|
|
||||||
// recreated.
|
|
||||||
if (returnFiber.lastEffect !== null) {
|
|
||||||
returnFiber.lastEffect.nextEffect = childToDelete;
|
|
||||||
returnFiber.lastEffect = childToDelete;
|
|
||||||
} else {
|
|
||||||
returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
|
|
||||||
}
|
|
||||||
|
|
||||||
const deletions = returnFiber.deletions;
|
const deletions = returnFiber.deletions;
|
||||||
if (deletions === null) {
|
if (deletions === null) {
|
||||||
|
|
|
@ -60,9 +60,6 @@ export type SuspenseListRenderState = {|
|
||||||
tail: null | Fiber,
|
tail: null | Fiber,
|
||||||
// Tail insertions setting.
|
// Tail insertions setting.
|
||||||
tailMode: SuspenseListTailMode,
|
tailMode: SuspenseListTailMode,
|
||||||
// Last Effect before we rendered the "rendering" item.
|
|
||||||
// Used to remove new effects added by the rendered item.
|
|
||||||
lastEffect: null | Fiber,
|
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export function shouldCaptureSuspense(
|
export function shouldCaptureSuspense(
|
||||||
|
|
|
@ -188,8 +188,6 @@ function throwException(
|
||||||
) {
|
) {
|
||||||
// The source fiber did not complete.
|
// The source fiber did not complete.
|
||||||
sourceFiber.flags |= Incomplete;
|
sourceFiber.flags |= Incomplete;
|
||||||
// Its effect list is no longer valid.
|
|
||||||
sourceFiber.firstEffect = sourceFiber.lastEffect = null;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
value !== null &&
|
value !== null &&
|
||||||
|
|
|
@ -13,7 +13,6 @@ import type {Lanes, Lane} from './ReactFiberLane.old';
|
||||||
import type {ReactPriorityLevel} from './ReactInternalTypes';
|
import type {ReactPriorityLevel} from './ReactInternalTypes';
|
||||||
import type {Interaction} from 'scheduler/src/Tracing';
|
import type {Interaction} from 'scheduler/src/Tracing';
|
||||||
import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
|
import type {SuspenseState} from './ReactFiberSuspenseComponent.old';
|
||||||
import type {Effect as HookEffect} from './ReactFiberHooks.old';
|
|
||||||
import type {StackCursor} from './ReactFiberStack.old';
|
import type {StackCursor} from './ReactFiberStack.old';
|
||||||
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.old';
|
import type {FunctionComponentUpdateQueue} from './ReactFiberHooks.old';
|
||||||
import type {Flags} from './ReactFiberFlags';
|
import type {Flags} from './ReactFiberFlags';
|
||||||
|
@ -85,13 +84,11 @@ import * as Scheduler from 'scheduler';
|
||||||
import {__interactionsRef, __subscriberRef} from 'scheduler/tracing';
|
import {__interactionsRef, __subscriberRef} from 'scheduler/tracing';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
prepareForCommit,
|
|
||||||
resetAfterCommit,
|
resetAfterCommit,
|
||||||
scheduleTimeout,
|
scheduleTimeout,
|
||||||
cancelTimeout,
|
cancelTimeout,
|
||||||
noTimeout,
|
noTimeout,
|
||||||
warnsIfNotActing,
|
warnsIfNotActing,
|
||||||
beforeActiveInstanceBlur,
|
|
||||||
afterActiveInstanceBlur,
|
afterActiveInstanceBlur,
|
||||||
clearContainer,
|
clearContainer,
|
||||||
} from './ReactFiberHostConfig';
|
} from './ReactFiberHostConfig';
|
||||||
|
@ -122,17 +119,15 @@ import {
|
||||||
import {LegacyRoot} from './ReactRootTags';
|
import {LegacyRoot} from './ReactRootTags';
|
||||||
import {
|
import {
|
||||||
NoFlags,
|
NoFlags,
|
||||||
PerformedWork,
|
|
||||||
Placement,
|
Placement,
|
||||||
Deletion,
|
|
||||||
ChildDeletion,
|
|
||||||
Snapshot,
|
|
||||||
Passive,
|
|
||||||
PassiveStatic,
|
PassiveStatic,
|
||||||
Incomplete,
|
Incomplete,
|
||||||
HostEffectMask,
|
HostEffectMask,
|
||||||
Hydrating,
|
Hydrating,
|
||||||
StaticMask,
|
BeforeMutationMask,
|
||||||
|
MutationMask,
|
||||||
|
LayoutMask,
|
||||||
|
PassiveMask,
|
||||||
MountPassiveDev,
|
MountPassiveDev,
|
||||||
MountLayoutDev,
|
MountLayoutDev,
|
||||||
} from './ReactFiberFlags';
|
} from './ReactFiberFlags';
|
||||||
|
@ -185,14 +180,12 @@ import {
|
||||||
createClassErrorUpdate,
|
createClassErrorUpdate,
|
||||||
} from './ReactFiberThrow.old';
|
} from './ReactFiberThrow.old';
|
||||||
import {
|
import {
|
||||||
commitBeforeMutationLifeCycles as commitBeforeMutationEffectOnFiber,
|
commitBeforeMutationEffects,
|
||||||
commitLayoutEffects,
|
commitLayoutEffects,
|
||||||
commitMutationEffects,
|
commitMutationEffects,
|
||||||
commitPassiveEffectDurations,
|
commitPassiveEffectDurations,
|
||||||
isSuspenseBoundaryBeingHidden,
|
|
||||||
commitPassiveMountEffects,
|
commitPassiveMountEffects,
|
||||||
commitPassiveUnmountEffects,
|
commitPassiveUnmountEffects,
|
||||||
detachFiberAfterEffects,
|
|
||||||
invokeLayoutEffectMountInDEV,
|
invokeLayoutEffectMountInDEV,
|
||||||
invokePassiveEffectMountInDEV,
|
invokePassiveEffectMountInDEV,
|
||||||
invokeLayoutEffectUnmountInDEV,
|
invokeLayoutEffectUnmountInDEV,
|
||||||
|
@ -240,7 +233,6 @@ import {onCommitRoot as onCommitRootTestSelector} from './ReactTestSelectors';
|
||||||
|
|
||||||
// Used by `act`
|
// Used by `act`
|
||||||
import enqueueTask from 'shared/enqueueTask';
|
import enqueueTask from 'shared/enqueueTask';
|
||||||
import {doesFiberContain} from './ReactFiberTreeReflection';
|
|
||||||
|
|
||||||
const ceil = Math.ceil;
|
const ceil = Math.ceil;
|
||||||
|
|
||||||
|
@ -328,7 +320,6 @@ export function getRenderTargetTime(): number {
|
||||||
return workInProgressRootRenderTargetTime;
|
return workInProgressRootRenderTargetTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
let nextEffect: Fiber | null = null;
|
|
||||||
let hasUncaughtError = false;
|
let hasUncaughtError = false;
|
||||||
let firstUncaughtError = null;
|
let firstUncaughtError = null;
|
||||||
let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;
|
let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;
|
||||||
|
@ -371,9 +362,6 @@ let currentEventPendingLanes: Lanes = NoLanes;
|
||||||
// We warn about state updates for unmounted components differently in this case.
|
// We warn about state updates for unmounted components differently in this case.
|
||||||
let isFlushingPassiveEffects = false;
|
let isFlushingPassiveEffects = false;
|
||||||
|
|
||||||
let focusedInstanceHandle: null | Fiber = null;
|
|
||||||
let shouldFireAfterActiveInstanceBlur: boolean = false;
|
|
||||||
|
|
||||||
export function getWorkInProgressRoot(): FiberRoot | null {
|
export function getWorkInProgressRoot(): FiberRoot | null {
|
||||||
return workInProgressRoot;
|
return workInProgressRoot;
|
||||||
}
|
}
|
||||||
|
@ -1737,45 +1725,6 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
|
||||||
workInProgress = next;
|
workInProgress = next;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
returnFiber !== null &&
|
|
||||||
// Do not append effects to parents if a sibling failed to complete
|
|
||||||
(returnFiber.flags & Incomplete) === NoFlags
|
|
||||||
) {
|
|
||||||
// Append all the effects of the subtree and this fiber onto the effect
|
|
||||||
// list of the parent. The completion order of the children affects the
|
|
||||||
// side-effect order.
|
|
||||||
if (returnFiber.firstEffect === null) {
|
|
||||||
returnFiber.firstEffect = completedWork.firstEffect;
|
|
||||||
}
|
|
||||||
if (completedWork.lastEffect !== null) {
|
|
||||||
if (returnFiber.lastEffect !== null) {
|
|
||||||
returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
|
|
||||||
}
|
|
||||||
returnFiber.lastEffect = completedWork.lastEffect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this fiber had side-effects, we append it AFTER the children's
|
|
||||||
// side-effects. We can perform certain side-effects earlier if needed,
|
|
||||||
// by doing multiple passes over the effect list. We don't want to
|
|
||||||
// schedule our own side-effect on our own list because if end up
|
|
||||||
// reusing children we'll schedule this effect onto itself since we're
|
|
||||||
// at the end.
|
|
||||||
const flags = completedWork.flags;
|
|
||||||
|
|
||||||
// Skip both NoWork and PerformedWork tags when creating the effect
|
|
||||||
// list. PerformedWork effect is read by React DevTools but shouldn't be
|
|
||||||
// committed.
|
|
||||||
if ((flags & ~StaticMask) > PerformedWork) {
|
|
||||||
if (returnFiber.lastEffect !== null) {
|
|
||||||
returnFiber.lastEffect.nextEffect = completedWork;
|
|
||||||
} else {
|
|
||||||
returnFiber.firstEffect = completedWork;
|
|
||||||
}
|
|
||||||
returnFiber.lastEffect = completedWork;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// This fiber did not complete because something threw. Pop values off
|
// This fiber did not complete because something threw. Pop values off
|
||||||
// the stack without entering the complete phase. If this is a boundary,
|
// the stack without entering the complete phase. If this is a boundary,
|
||||||
|
@ -1812,8 +1761,7 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnFiber !== null) {
|
if (returnFiber !== null) {
|
||||||
// Mark the parent fiber as incomplete and clear its effect list.
|
// Mark the parent fiber as incomplete and clear its subtree flags.
|
||||||
returnFiber.firstEffect = returnFiber.lastEffect = null;
|
|
||||||
returnFiber.flags |= Incomplete;
|
returnFiber.flags |= Incomplete;
|
||||||
returnFiber.subtreeFlags = NoFlags;
|
returnFiber.subtreeFlags = NoFlags;
|
||||||
returnFiber.deletions = null;
|
returnFiber.deletions = null;
|
||||||
|
@ -1931,25 +1879,39 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||||
// times out.
|
// times out.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the list of effects.
|
// If there are pending passive effects, schedule a callback to process them.
|
||||||
let firstEffect;
|
// Do this as early as possible, so it is queued before anything else that
|
||||||
if (finishedWork.flags > PerformedWork) {
|
// might get scheduled in the commit phase. (See #16714.)
|
||||||
// A fiber's effect list consists only of its children, not itself. So if
|
// TODO: Delete all other places that schedule the passive effect callback
|
||||||
// the root has an effect, we need to add it to the end of the list. The
|
// They're redundant.
|
||||||
// resulting list is the set that would belong to the root's parent, if it
|
if (
|
||||||
// had one; that is, all the effects in the tree including the root.
|
(finishedWork.subtreeFlags & PassiveMask) !== NoFlags ||
|
||||||
if (finishedWork.lastEffect !== null) {
|
(finishedWork.flags & PassiveMask) !== NoFlags
|
||||||
finishedWork.lastEffect.nextEffect = finishedWork;
|
) {
|
||||||
firstEffect = finishedWork.firstEffect;
|
if (!rootDoesHavePassiveEffects) {
|
||||||
} else {
|
rootDoesHavePassiveEffects = true;
|
||||||
firstEffect = finishedWork;
|
scheduleCallback(NormalSchedulerPriority, () => {
|
||||||
|
flushPassiveEffects();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// There is no effect on the root.
|
|
||||||
firstEffect = finishedWork.firstEffect;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstEffect !== null) {
|
// Check if there are any effects in the whole tree.
|
||||||
|
// TODO: This is left over from the effect list implementation, where we had
|
||||||
|
// to check for the existence of `firstEffect` to satsify Flow. I think the
|
||||||
|
// only other reason this optimization exists is because it affects profiling.
|
||||||
|
// Reconsider whether this is necessary.
|
||||||
|
const subtreeHasEffects =
|
||||||
|
(finishedWork.subtreeFlags &
|
||||||
|
(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
|
||||||
|
NoFlags;
|
||||||
|
const rootHasEffect =
|
||||||
|
(finishedWork.flags &
|
||||||
|
(BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
|
||||||
|
NoFlags;
|
||||||
|
|
||||||
|
if (subtreeHasEffects || rootHasEffect) {
|
||||||
let previousLanePriority;
|
let previousLanePriority;
|
||||||
if (decoupleUpdatePriorityFromScheduler) {
|
if (decoupleUpdatePriorityFromScheduler) {
|
||||||
previousLanePriority = getCurrentUpdateLanePriority();
|
previousLanePriority = getCurrentUpdateLanePriority();
|
||||||
|
@ -1970,32 +1932,10 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||||
// The first phase a "before mutation" phase. We use this phase to read the
|
// The first phase a "before mutation" phase. We use this phase to read the
|
||||||
// state of the host tree right before we mutate it. This is where
|
// state of the host tree right before we mutate it. This is where
|
||||||
// getSnapshotBeforeUpdate is called.
|
// getSnapshotBeforeUpdate is called.
|
||||||
focusedInstanceHandle = prepareForCommit(root.containerInfo);
|
const shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
|
||||||
shouldFireAfterActiveInstanceBlur = false;
|
root,
|
||||||
|
finishedWork,
|
||||||
nextEffect = firstEffect;
|
);
|
||||||
do {
|
|
||||||
if (__DEV__) {
|
|
||||||
invokeGuardedCallback(null, commitBeforeMutationEffects, null);
|
|
||||||
if (hasCaughtError()) {
|
|
||||||
invariant(nextEffect !== null, 'Should be working on an effect.');
|
|
||||||
const error = clearCaughtError();
|
|
||||||
captureCommitPhaseError(nextEffect, error);
|
|
||||||
nextEffect = nextEffect.nextEffect;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
commitBeforeMutationEffects();
|
|
||||||
} catch (error) {
|
|
||||||
invariant(nextEffect !== null, 'Should be working on an effect.');
|
|
||||||
captureCommitPhaseError(nextEffect, error);
|
|
||||||
nextEffect = nextEffect.nextEffect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (nextEffect !== null);
|
|
||||||
|
|
||||||
// We no longer need to track the active instance fiber
|
|
||||||
focusedInstanceHandle = null;
|
|
||||||
|
|
||||||
if (enableProfilerTimer) {
|
if (enableProfilerTimer) {
|
||||||
// Mark the current commit time to be shared by all Profilers in this
|
// Mark the current commit time to be shared by all Profilers in this
|
||||||
|
@ -2082,31 +2022,6 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||||
rootWithPendingPassiveEffects = root;
|
rootWithPendingPassiveEffects = root;
|
||||||
pendingPassiveEffectsLanes = lanes;
|
pendingPassiveEffectsLanes = lanes;
|
||||||
pendingPassiveEffectsRenderPriority = renderPriorityLevel;
|
pendingPassiveEffectsRenderPriority = renderPriorityLevel;
|
||||||
} else {
|
|
||||||
// We are done with the effect chain at this point so let's clear the
|
|
||||||
// nextEffect pointers to assist with GC. If we have passive effects, we'll
|
|
||||||
// clear this in flushPassiveEffects
|
|
||||||
// TODO: We should always do this in the passive phase, by scheduling
|
|
||||||
// a passive callback for every deletion.
|
|
||||||
nextEffect = firstEffect;
|
|
||||||
while (nextEffect !== null) {
|
|
||||||
const nextNextEffect = nextEffect.nextEffect;
|
|
||||||
nextEffect.nextEffect = null;
|
|
||||||
if (nextEffect.flags & ChildDeletion) {
|
|
||||||
const deletions = nextEffect.deletions;
|
|
||||||
if (deletions !== null) {
|
|
||||||
for (let i = 0; i < deletions.length; i++) {
|
|
||||||
const deletion = deletions[i];
|
|
||||||
const alternate = deletion.alternate;
|
|
||||||
detachFiberAfterEffects(deletion);
|
|
||||||
if (alternate !== null) {
|
|
||||||
detachFiberAfterEffects(alternate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nextEffect = nextNextEffect;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read this again, since an effect might have updated it
|
// Read this again, since an effect might have updated it
|
||||||
|
@ -2218,52 +2133,6 @@ function commitRootImpl(root, renderPriorityLevel) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function commitBeforeMutationEffects() {
|
|
||||||
while (nextEffect !== null) {
|
|
||||||
const current = nextEffect.alternate;
|
|
||||||
|
|
||||||
if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
|
|
||||||
if ((nextEffect.flags & Deletion) !== NoFlags) {
|
|
||||||
if (doesFiberContain(nextEffect, focusedInstanceHandle)) {
|
|
||||||
shouldFireAfterActiveInstanceBlur = true;
|
|
||||||
beforeActiveInstanceBlur(nextEffect);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// TODO: Move this out of the hot path using a dedicated effect tag.
|
|
||||||
if (
|
|
||||||
nextEffect.tag === SuspenseComponent &&
|
|
||||||
isSuspenseBoundaryBeingHidden(current, nextEffect) &&
|
|
||||||
doesFiberContain(nextEffect, focusedInstanceHandle)
|
|
||||||
) {
|
|
||||||
shouldFireAfterActiveInstanceBlur = true;
|
|
||||||
beforeActiveInstanceBlur(nextEffect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const flags = nextEffect.flags;
|
|
||||||
if ((flags & Snapshot) !== NoFlags) {
|
|
||||||
setCurrentDebugFiberInDEV(nextEffect);
|
|
||||||
|
|
||||||
commitBeforeMutationEffectOnFiber(current, nextEffect);
|
|
||||||
|
|
||||||
resetCurrentDebugFiberInDEV();
|
|
||||||
}
|
|
||||||
if ((flags & Passive) !== NoFlags) {
|
|
||||||
// If there are passive effects, schedule a callback to flush at
|
|
||||||
// the earliest opportunity.
|
|
||||||
if (!rootDoesHavePassiveEffects) {
|
|
||||||
rootDoesHavePassiveEffects = true;
|
|
||||||
scheduleCallback(NormalSchedulerPriority, () => {
|
|
||||||
flushPassiveEffects();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nextEffect = nextEffect.nextEffect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function flushPassiveEffects(): boolean {
|
export function flushPassiveEffects(): boolean {
|
||||||
// Returns whether passive effects were flushed.
|
// Returns whether passive effects were flushed.
|
||||||
if (pendingPassiveEffectsRenderPriority !== NoSchedulerPriority) {
|
if (pendingPassiveEffectsRenderPriority !== NoSchedulerPriority) {
|
||||||
|
@ -2302,32 +2171,6 @@ export function enqueuePendingPassiveProfilerEffect(fiber: Fiber): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function enqueuePendingPassiveHookEffectMount(
|
|
||||||
fiber: Fiber,
|
|
||||||
effect: HookEffect,
|
|
||||||
): void {
|
|
||||||
if (!rootDoesHavePassiveEffects) {
|
|
||||||
rootDoesHavePassiveEffects = true;
|
|
||||||
scheduleCallback(NormalSchedulerPriority, () => {
|
|
||||||
flushPassiveEffects();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function enqueuePendingPassiveHookEffectUnmount(
|
|
||||||
fiber: Fiber,
|
|
||||||
effect: HookEffect,
|
|
||||||
): void {
|
|
||||||
if (!rootDoesHavePassiveEffects) {
|
|
||||||
rootDoesHavePassiveEffects = true;
|
|
||||||
scheduleCallback(NormalSchedulerPriority, () => {
|
|
||||||
flushPassiveEffects();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function flushPassiveEffectsImpl() {
|
function flushPassiveEffectsImpl() {
|
||||||
if (rootWithPendingPassiveEffects === null) {
|
if (rootWithPendingPassiveEffects === null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue