mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Cleaned up passive effects experimental flags (#19021)
This commit is contained in:
@@ -27,7 +27,6 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent';
|
||||
|
||||
import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
|
||||
import {
|
||||
deferPassiveEffectCleanupDuringUnmount,
|
||||
enableSchedulerTracing,
|
||||
enableProfilerTimer,
|
||||
enableProfilerCommitHooks,
|
||||
@@ -36,7 +35,6 @@ import {
|
||||
enableFundamentalAPI,
|
||||
enableSuspenseCallback,
|
||||
enableScopeAPI,
|
||||
runAllPassiveEffectDestroysBeforeCreates,
|
||||
enableCreateEventHandleAPI,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
@@ -71,7 +69,6 @@ import {
|
||||
Placement,
|
||||
Snapshot,
|
||||
Update,
|
||||
Passive,
|
||||
} from './ReactSideEffectTags';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import invariant from 'shared/invariant';
|
||||
@@ -81,9 +78,7 @@ import {resolveDefaultProps} from './ReactFiberLazyComponent.new';
|
||||
import {
|
||||
getCommitTime,
|
||||
recordLayoutEffectDuration,
|
||||
recordPassiveEffectDuration,
|
||||
startLayoutEffectTimer,
|
||||
startPassiveEffectTimer,
|
||||
} from './ReactProfilerTimer.new';
|
||||
import {ProfileMode} from './ReactTypeOfMode';
|
||||
import {commitUpdateQueue} from './ReactUpdateQueue.new';
|
||||
@@ -134,10 +129,6 @@ import {
|
||||
Passive as HookPassive,
|
||||
} from './ReactHookEffectTags';
|
||||
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.new';
|
||||
import {
|
||||
runWithPriority,
|
||||
NormalPriority,
|
||||
} from './SchedulerWithReactIntegration.new';
|
||||
import {
|
||||
updateDeprecatedEventListeners,
|
||||
unmountDeprecatedResponderListeners,
|
||||
@@ -394,67 +385,22 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
|
||||
}
|
||||
|
||||
function schedulePassiveEffects(finishedWork: Fiber) {
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
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 commitPassiveHookEffects(finishedWork: Fiber): void {
|
||||
if ((finishedWork.effectTag & Passive) !== NoEffect) {
|
||||
switch (finishedWork.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case SimpleMemoComponent:
|
||||
case Block: {
|
||||
// TODO (#17945) We should call all passive destroy functions (for all fibers)
|
||||
// before calling any create functions. The current approach only serializes
|
||||
// these for a single fiber.
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
commitHookEffectListUnmount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
commitHookEffectListMount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
} finally {
|
||||
recordPassiveEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
|
||||
}
|
||||
break;
|
||||
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);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
effect = next;
|
||||
} while (effect !== firstEffect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,9 +489,7 @@ function commitLifeCycles(
|
||||
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
|
||||
}
|
||||
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
schedulePassiveEffects(finishedWork);
|
||||
}
|
||||
schedulePassiveEffects(finishedWork);
|
||||
return;
|
||||
}
|
||||
case ClassComponent: {
|
||||
@@ -946,74 +890,28 @@ function commitUnmount(
|
||||
if (lastEffect !== null) {
|
||||
const firstEffect = lastEffect.next;
|
||||
|
||||
if (
|
||||
deferPassiveEffectCleanupDuringUnmount &&
|
||||
runAllPassiveEffectDestroysBeforeCreates
|
||||
) {
|
||||
let effect = firstEffect;
|
||||
do {
|
||||
const {destroy, tag} = effect;
|
||||
if (destroy !== undefined) {
|
||||
if ((tag & HookPassive) !== NoHookEffect) {
|
||||
enqueuePendingPassiveHookEffectUnmount(current, effect);
|
||||
let effect = firstEffect;
|
||||
do {
|
||||
const {destroy, tag} = effect;
|
||||
if (destroy !== undefined) {
|
||||
if ((tag & HookPassive) !== NoHookEffect) {
|
||||
enqueuePendingPassiveHookEffectUnmount(current, effect);
|
||||
} else {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
startLayoutEffectTimer();
|
||||
safelyCallDestroy(current, destroy);
|
||||
recordLayoutEffectDuration(current);
|
||||
} else {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
startLayoutEffectTimer();
|
||||
safelyCallDestroy(current, destroy);
|
||||
recordLayoutEffectDuration(current);
|
||||
} else {
|
||||
safelyCallDestroy(current, destroy);
|
||||
}
|
||||
safelyCallDestroy(current, destroy);
|
||||
}
|
||||
}
|
||||
effect = effect.next;
|
||||
} while (effect !== firstEffect);
|
||||
} else {
|
||||
// When the owner fiber is deleted, the destroy function of a passive
|
||||
// effect hook is called during the synchronous commit phase. This is
|
||||
// a concession to implementation complexity. Calling it in the
|
||||
// passive effect phase (like they usually are, when dependencies
|
||||
// change during an update) would require either traversing the
|
||||
// children of the deleted fiber again, or including unmount effects
|
||||
// as part of the fiber effect list.
|
||||
//
|
||||
// Because this is during the sync commit phase, we need to change
|
||||
// the priority.
|
||||
//
|
||||
// TODO: Reconsider this implementation trade off.
|
||||
const priorityLevel =
|
||||
renderPriorityLevel > NormalPriority
|
||||
? NormalPriority
|
||||
: renderPriorityLevel;
|
||||
runWithPriority(priorityLevel, () => {
|
||||
let effect = firstEffect;
|
||||
do {
|
||||
const {destroy, tag} = effect;
|
||||
if (destroy !== undefined) {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
if ((tag & HookPassive) !== NoHookEffect) {
|
||||
safelyCallDestroy(current, destroy);
|
||||
} else {
|
||||
startLayoutEffectTimer();
|
||||
safelyCallDestroy(current, destroy);
|
||||
recordLayoutEffectDuration(current);
|
||||
}
|
||||
} else {
|
||||
safelyCallDestroy(current, destroy);
|
||||
}
|
||||
}
|
||||
effect = effect.next;
|
||||
} while (effect !== firstEffect);
|
||||
});
|
||||
}
|
||||
}
|
||||
effect = effect.next;
|
||||
} while (effect !== firstEffect);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -26,7 +26,6 @@ import type {ReactPriorityLevel} from './ReactInternalTypes';
|
||||
|
||||
import {unstable_wrap as Schedule_tracing_wrap} from 'scheduler/tracing';
|
||||
import {
|
||||
deferPassiveEffectCleanupDuringUnmount,
|
||||
enableSchedulerTracing,
|
||||
enableProfilerTimer,
|
||||
enableProfilerCommitHooks,
|
||||
@@ -35,7 +34,6 @@ import {
|
||||
enableFundamentalAPI,
|
||||
enableSuspenseCallback,
|
||||
enableScopeAPI,
|
||||
runAllPassiveEffectDestroysBeforeCreates,
|
||||
enableCreateEventHandleAPI,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
@@ -68,7 +66,6 @@ import {
|
||||
Placement,
|
||||
Snapshot,
|
||||
Update,
|
||||
Passive,
|
||||
} from './ReactSideEffectTags';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import invariant from 'shared/invariant';
|
||||
@@ -78,9 +75,7 @@ import {resolveDefaultProps} from './ReactFiberLazyComponent.old';
|
||||
import {
|
||||
getCommitTime,
|
||||
recordLayoutEffectDuration,
|
||||
recordPassiveEffectDuration,
|
||||
startLayoutEffectTimer,
|
||||
startPassiveEffectTimer,
|
||||
} from './ReactProfilerTimer.old';
|
||||
import {ProfileMode} from './ReactTypeOfMode';
|
||||
import {commitUpdateQueue} from './ReactUpdateQueue.old';
|
||||
@@ -131,10 +126,6 @@ import {
|
||||
Passive as HookPassive,
|
||||
} from './ReactHookEffectTags';
|
||||
import {didWarnAboutReassigningProps} from './ReactFiberBeginWork.old';
|
||||
import {
|
||||
runWithPriority,
|
||||
NormalPriority,
|
||||
} from './SchedulerWithReactIntegration.old';
|
||||
import {
|
||||
updateDeprecatedEventListeners,
|
||||
unmountDeprecatedResponderListeners,
|
||||
@@ -391,67 +382,22 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
|
||||
}
|
||||
|
||||
function schedulePassiveEffects(finishedWork: Fiber) {
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
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 commitPassiveHookEffects(finishedWork: Fiber): void {
|
||||
if ((finishedWork.effectTag & Passive) !== NoEffect) {
|
||||
switch (finishedWork.tag) {
|
||||
case FunctionComponent:
|
||||
case ForwardRef:
|
||||
case SimpleMemoComponent:
|
||||
case Block: {
|
||||
// TODO (#17945) We should call all passive destroy functions (for all fibers)
|
||||
// before calling any create functions. The current approach only serializes
|
||||
// these for a single fiber.
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
finishedWork.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
commitHookEffectListUnmount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
commitHookEffectListMount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
} finally {
|
||||
recordPassiveEffectDuration(finishedWork);
|
||||
}
|
||||
} else {
|
||||
commitHookEffectListUnmount(
|
||||
HookPassive | HookHasEffect,
|
||||
finishedWork,
|
||||
);
|
||||
commitHookEffectListMount(HookPassive | HookHasEffect, finishedWork);
|
||||
}
|
||||
break;
|
||||
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);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
effect = next;
|
||||
} while (effect !== firstEffect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,9 +486,7 @@ function commitLifeCycles(
|
||||
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
|
||||
}
|
||||
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
schedulePassiveEffects(finishedWork);
|
||||
}
|
||||
schedulePassiveEffects(finishedWork);
|
||||
return;
|
||||
}
|
||||
case ClassComponent: {
|
||||
@@ -944,74 +888,28 @@ function commitUnmount(
|
||||
if (lastEffect !== null) {
|
||||
const firstEffect = lastEffect.next;
|
||||
|
||||
if (
|
||||
deferPassiveEffectCleanupDuringUnmount &&
|
||||
runAllPassiveEffectDestroysBeforeCreates
|
||||
) {
|
||||
let effect = firstEffect;
|
||||
do {
|
||||
const {destroy, tag} = effect;
|
||||
if (destroy !== undefined) {
|
||||
if ((tag & HookPassive) !== NoHookEffect) {
|
||||
enqueuePendingPassiveHookEffectUnmount(current, effect);
|
||||
let effect = firstEffect;
|
||||
do {
|
||||
const {destroy, tag} = effect;
|
||||
if (destroy !== undefined) {
|
||||
if ((tag & HookPassive) !== NoHookEffect) {
|
||||
enqueuePendingPassiveHookEffectUnmount(current, effect);
|
||||
} else {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
startLayoutEffectTimer();
|
||||
safelyCallDestroy(current, destroy);
|
||||
recordLayoutEffectDuration(current);
|
||||
} else {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
startLayoutEffectTimer();
|
||||
safelyCallDestroy(current, destroy);
|
||||
recordLayoutEffectDuration(current);
|
||||
} else {
|
||||
safelyCallDestroy(current, destroy);
|
||||
}
|
||||
safelyCallDestroy(current, destroy);
|
||||
}
|
||||
}
|
||||
effect = effect.next;
|
||||
} while (effect !== firstEffect);
|
||||
} else {
|
||||
// When the owner fiber is deleted, the destroy function of a passive
|
||||
// effect hook is called during the synchronous commit phase. This is
|
||||
// a concession to implementation complexity. Calling it in the
|
||||
// passive effect phase (like they usually are, when dependencies
|
||||
// change during an update) would require either traversing the
|
||||
// children of the deleted fiber again, or including unmount effects
|
||||
// as part of the fiber effect list.
|
||||
//
|
||||
// Because this is during the sync commit phase, we need to change
|
||||
// the priority.
|
||||
//
|
||||
// TODO: Reconsider this implementation trade off.
|
||||
const priorityLevel =
|
||||
renderPriorityLevel > NormalPriority
|
||||
? NormalPriority
|
||||
: renderPriorityLevel;
|
||||
runWithPriority(priorityLevel, () => {
|
||||
let effect = firstEffect;
|
||||
do {
|
||||
const {destroy, tag} = effect;
|
||||
if (destroy !== undefined) {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
current.mode & ProfileMode
|
||||
) {
|
||||
if ((tag & HookPassive) !== NoHookEffect) {
|
||||
safelyCallDestroy(current, destroy);
|
||||
} else {
|
||||
startLayoutEffectTimer();
|
||||
safelyCallDestroy(current, destroy);
|
||||
recordLayoutEffectDuration(current);
|
||||
}
|
||||
} else {
|
||||
safelyCallDestroy(current, destroy);
|
||||
}
|
||||
}
|
||||
effect = effect.next;
|
||||
} while (effect !== firstEffect);
|
||||
});
|
||||
}
|
||||
}
|
||||
effect = effect.next;
|
||||
} while (effect !== firstEffect);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -19,8 +19,6 @@ import type {StackCursor} from './ReactFiberStack.new';
|
||||
|
||||
import {
|
||||
warnAboutDeprecatedLifecycles,
|
||||
deferPassiveEffectCleanupDuringUnmount,
|
||||
runAllPassiveEffectDestroysBeforeCreates,
|
||||
enableSuspenseServerRenderer,
|
||||
replayFailedUnitOfWorkWithInvokeGuardedCallback,
|
||||
enableProfilerTimer,
|
||||
@@ -154,7 +152,6 @@ import {
|
||||
import {
|
||||
commitBeforeMutationLifeCycles as commitBeforeMutationEffectOnFiber,
|
||||
commitLifeCycles as commitLayoutEffectOnFiber,
|
||||
commitPassiveHookEffects,
|
||||
commitPlacement,
|
||||
commitWork,
|
||||
commitDeletion,
|
||||
@@ -2260,15 +2257,13 @@ export function enqueuePendingPassiveHookEffectMount(
|
||||
fiber: Fiber,
|
||||
effect: HookEffect,
|
||||
): void {
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
pendingPassiveHookEffectsMount.push(effect, fiber);
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalSchedulerPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
pendingPassiveHookEffectsMount.push(effect, fiber);
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalSchedulerPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2276,25 +2271,21 @@ export function enqueuePendingPassiveHookEffectUnmount(
|
||||
fiber: Fiber,
|
||||
effect: HookEffect,
|
||||
): void {
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
pendingPassiveHookEffectsUnmount.push(effect, fiber);
|
||||
if (__DEV__) {
|
||||
if (deferPassiveEffectCleanupDuringUnmount) {
|
||||
fiber.effectTag |= PassiveUnmountPendingDev;
|
||||
const alternate = fiber.alternate;
|
||||
if (alternate !== null) {
|
||||
alternate.effectTag |= PassiveUnmountPendingDev;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalSchedulerPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
pendingPassiveHookEffectsUnmount.push(effect, fiber);
|
||||
if (__DEV__) {
|
||||
fiber.effectTag |= PassiveUnmountPendingDev;
|
||||
const alternate = fiber.alternate;
|
||||
if (alternate !== null) {
|
||||
alternate.effectTag |= PassiveUnmountPendingDev;
|
||||
}
|
||||
}
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalSchedulerPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function invokePassiveEffectCreate(effect: HookEffect): void {
|
||||
@@ -2325,82 +2316,31 @@ function flushPassiveEffectsImpl() {
|
||||
executionContext |= CommitContext;
|
||||
const prevInteractions = pushInteractions(root);
|
||||
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
// It's important that ALL pending passive effect destroy functions are called
|
||||
// before ANY passive effect create functions are called.
|
||||
// Otherwise effects in sibling components might interfere with each other.
|
||||
// e.g. a destroy function in one component may unintentionally override a ref
|
||||
// value set by a create function in another component.
|
||||
// Layout effects have the same constraint.
|
||||
// It's important that ALL pending passive effect destroy functions are called
|
||||
// before ANY passive effect create functions are called.
|
||||
// Otherwise effects in sibling components might interfere with each other.
|
||||
// e.g. a destroy function in one component may unintentionally override a ref
|
||||
// value set by a create function in another component.
|
||||
// Layout effects have the same constraint.
|
||||
|
||||
// First pass: Destroy stale passive effects.
|
||||
const unmountEffects = pendingPassiveHookEffectsUnmount;
|
||||
pendingPassiveHookEffectsUnmount = [];
|
||||
for (let i = 0; i < unmountEffects.length; i += 2) {
|
||||
const effect = ((unmountEffects[i]: any): HookEffect);
|
||||
const fiber = ((unmountEffects[i + 1]: any): Fiber);
|
||||
const destroy = effect.destroy;
|
||||
effect.destroy = undefined;
|
||||
// First pass: Destroy stale passive effects.
|
||||
const unmountEffects = pendingPassiveHookEffectsUnmount;
|
||||
pendingPassiveHookEffectsUnmount = [];
|
||||
for (let i = 0; i < unmountEffects.length; i += 2) {
|
||||
const effect = ((unmountEffects[i]: any): HookEffect);
|
||||
const fiber = ((unmountEffects[i + 1]: any): Fiber);
|
||||
const destroy = effect.destroy;
|
||||
effect.destroy = undefined;
|
||||
|
||||
if (__DEV__) {
|
||||
if (deferPassiveEffectCleanupDuringUnmount) {
|
||||
fiber.effectTag &= ~PassiveUnmountPendingDev;
|
||||
const alternate = fiber.alternate;
|
||||
if (alternate !== null) {
|
||||
alternate.effectTag &= ~PassiveUnmountPendingDev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof destroy === 'function') {
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
startPassiveEffectTimer();
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
recordPassiveEffectDuration(fiber);
|
||||
} else {
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
}
|
||||
if (hasCaughtError()) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
const error = clearCaughtError();
|
||||
captureCommitPhaseError(fiber, error);
|
||||
}
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
destroy();
|
||||
} finally {
|
||||
recordPassiveEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
destroy();
|
||||
}
|
||||
} catch (error) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
captureCommitPhaseError(fiber, error);
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
fiber.effectTag &= ~PassiveUnmountPendingDev;
|
||||
const alternate = fiber.alternate;
|
||||
if (alternate !== null) {
|
||||
alternate.effectTag &= ~PassiveUnmountPendingDev;
|
||||
}
|
||||
}
|
||||
// Second pass: Create new passive effects.
|
||||
const mountEffects = pendingPassiveHookEffectsMount;
|
||||
pendingPassiveHookEffectsMount = [];
|
||||
for (let i = 0; i < mountEffects.length; i += 2) {
|
||||
const effect = ((mountEffects[i]: any): HookEffect);
|
||||
const fiber = ((mountEffects[i + 1]: any): Fiber);
|
||||
|
||||
if (typeof destroy === 'function') {
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
if (
|
||||
@@ -2409,10 +2349,10 @@ function flushPassiveEffectsImpl() {
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
startPassiveEffectTimer();
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
recordPassiveEffectDuration(fiber);
|
||||
} else {
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
}
|
||||
if (hasCaughtError()) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
@@ -2422,7 +2362,6 @@ function flushPassiveEffectsImpl() {
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
const create = effect.create;
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
@@ -2430,12 +2369,12 @@ function flushPassiveEffectsImpl() {
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
effect.destroy = create();
|
||||
destroy();
|
||||
} finally {
|
||||
recordPassiveEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
effect.destroy = create();
|
||||
destroy();
|
||||
}
|
||||
} catch (error) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
@@ -2444,33 +2383,60 @@ function flushPassiveEffectsImpl() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second pass: Create new passive effects.
|
||||
const mountEffects = pendingPassiveHookEffectsMount;
|
||||
pendingPassiveHookEffectsMount = [];
|
||||
for (let i = 0; i < mountEffects.length; i += 2) {
|
||||
const effect = ((mountEffects[i]: any): HookEffect);
|
||||
const fiber = ((mountEffects[i + 1]: any): Fiber);
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
startPassiveEffectTimer();
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
recordPassiveEffectDuration(fiber);
|
||||
} else {
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
}
|
||||
if (hasCaughtError()) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
const error = clearCaughtError();
|
||||
captureCommitPhaseError(fiber, error);
|
||||
}
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
const create = effect.create;
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
effect.destroy = create();
|
||||
} finally {
|
||||
recordPassiveEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
effect.destroy = create();
|
||||
}
|
||||
} catch (error) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
captureCommitPhaseError(fiber, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: This currently assumes there are no passive effects on the root fiber
|
||||
// because the root is not part of its own effect list.
|
||||
// This could change in the future.
|
||||
let effect = root.current.firstEffect;
|
||||
while (effect !== null) {
|
||||
// We do this work above if this flag is enabled, so we shouldn't be
|
||||
// doing it here.
|
||||
if (!runAllPassiveEffectDestroysBeforeCreates) {
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(effect);
|
||||
invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);
|
||||
if (hasCaughtError()) {
|
||||
invariant(effect !== null, 'Should be working on an effect.');
|
||||
const error = clearCaughtError();
|
||||
captureCommitPhaseError(effect, error);
|
||||
}
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
commitPassiveHookEffects(effect);
|
||||
} catch (error) {
|
||||
invariant(effect !== null, 'Should be working on an effect.');
|
||||
captureCommitPhaseError(effect, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nextNextEffect = effect.nextEffect;
|
||||
// Remove nextEffect pointer to assist GC
|
||||
effect.nextEffect = null;
|
||||
@@ -2868,15 +2834,10 @@ function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
deferPassiveEffectCleanupDuringUnmount &&
|
||||
runAllPassiveEffectDestroysBeforeCreates
|
||||
) {
|
||||
// If there are pending passive effects unmounts for this Fiber,
|
||||
// we can assume that they would have prevented this update.
|
||||
if ((fiber.effectTag & PassiveUnmountPendingDev) !== NoEffect) {
|
||||
return;
|
||||
}
|
||||
// If there are pending passive effects unmounts for this Fiber,
|
||||
// we can assume that they would have prevented this update.
|
||||
if ((fiber.effectTag & PassiveUnmountPendingDev) !== NoEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We show the whole stack but dedupe on the top component's name because
|
||||
|
||||
@@ -18,8 +18,6 @@ import type {Effect as HookEffect} from './ReactFiberHooks.old';
|
||||
|
||||
import {
|
||||
warnAboutDeprecatedLifecycles,
|
||||
deferPassiveEffectCleanupDuringUnmount,
|
||||
runAllPassiveEffectDestroysBeforeCreates,
|
||||
enableSuspenseServerRenderer,
|
||||
replayFailedUnitOfWorkWithInvokeGuardedCallback,
|
||||
enableProfilerTimer,
|
||||
@@ -151,7 +149,6 @@ import {
|
||||
import {
|
||||
commitBeforeMutationLifeCycles as commitBeforeMutationEffectOnFiber,
|
||||
commitLifeCycles as commitLayoutEffectOnFiber,
|
||||
commitPassiveHookEffects,
|
||||
commitPlacement,
|
||||
commitWork,
|
||||
commitDeletion,
|
||||
@@ -2400,15 +2397,13 @@ export function enqueuePendingPassiveHookEffectMount(
|
||||
fiber: Fiber,
|
||||
effect: HookEffect,
|
||||
): void {
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
pendingPassiveHookEffectsMount.push(effect, fiber);
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
pendingPassiveHookEffectsMount.push(effect, fiber);
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2416,25 +2411,21 @@ export function enqueuePendingPassiveHookEffectUnmount(
|
||||
fiber: Fiber,
|
||||
effect: HookEffect,
|
||||
): void {
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
pendingPassiveHookEffectsUnmount.push(effect, fiber);
|
||||
if (__DEV__) {
|
||||
if (deferPassiveEffectCleanupDuringUnmount) {
|
||||
fiber.effectTag |= PassiveUnmountPendingDev;
|
||||
const alternate = fiber.alternate;
|
||||
if (alternate !== null) {
|
||||
alternate.effectTag |= PassiveUnmountPendingDev;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
pendingPassiveHookEffectsUnmount.push(effect, fiber);
|
||||
if (__DEV__) {
|
||||
fiber.effectTag |= PassiveUnmountPendingDev;
|
||||
const alternate = fiber.alternate;
|
||||
if (alternate !== null) {
|
||||
alternate.effectTag |= PassiveUnmountPendingDev;
|
||||
}
|
||||
}
|
||||
if (!rootDoesHavePassiveEffects) {
|
||||
rootDoesHavePassiveEffects = true;
|
||||
scheduleCallback(NormalPriority, () => {
|
||||
flushPassiveEffects();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function invokePassiveEffectCreate(effect: HookEffect): void {
|
||||
@@ -2473,82 +2464,31 @@ function flushPassiveEffectsImpl() {
|
||||
executionContext |= CommitContext;
|
||||
const prevInteractions = pushInteractions(root);
|
||||
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
// It's important that ALL pending passive effect destroy functions are called
|
||||
// before ANY passive effect create functions are called.
|
||||
// Otherwise effects in sibling components might interfere with each other.
|
||||
// e.g. a destroy function in one component may unintentionally override a ref
|
||||
// value set by a create function in another component.
|
||||
// Layout effects have the same constraint.
|
||||
// It's important that ALL pending passive effect destroy functions are called
|
||||
// before ANY passive effect create functions are called.
|
||||
// Otherwise effects in sibling components might interfere with each other.
|
||||
// e.g. a destroy function in one component may unintentionally override a ref
|
||||
// value set by a create function in another component.
|
||||
// Layout effects have the same constraint.
|
||||
|
||||
// First pass: Destroy stale passive effects.
|
||||
const unmountEffects = pendingPassiveHookEffectsUnmount;
|
||||
pendingPassiveHookEffectsUnmount = [];
|
||||
for (let i = 0; i < unmountEffects.length; i += 2) {
|
||||
const effect = ((unmountEffects[i]: any): HookEffect);
|
||||
const fiber = ((unmountEffects[i + 1]: any): Fiber);
|
||||
const destroy = effect.destroy;
|
||||
effect.destroy = undefined;
|
||||
// First pass: Destroy stale passive effects.
|
||||
const unmountEffects = pendingPassiveHookEffectsUnmount;
|
||||
pendingPassiveHookEffectsUnmount = [];
|
||||
for (let i = 0; i < unmountEffects.length; i += 2) {
|
||||
const effect = ((unmountEffects[i]: any): HookEffect);
|
||||
const fiber = ((unmountEffects[i + 1]: any): Fiber);
|
||||
const destroy = effect.destroy;
|
||||
effect.destroy = undefined;
|
||||
|
||||
if (__DEV__) {
|
||||
if (deferPassiveEffectCleanupDuringUnmount) {
|
||||
fiber.effectTag &= ~PassiveUnmountPendingDev;
|
||||
const alternate = fiber.alternate;
|
||||
if (alternate !== null) {
|
||||
alternate.effectTag &= ~PassiveUnmountPendingDev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof destroy === 'function') {
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
startPassiveEffectTimer();
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
recordPassiveEffectDuration(fiber);
|
||||
} else {
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
}
|
||||
if (hasCaughtError()) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
const error = clearCaughtError();
|
||||
captureCommitPhaseError(fiber, error);
|
||||
}
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
destroy();
|
||||
} finally {
|
||||
recordPassiveEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
destroy();
|
||||
}
|
||||
} catch (error) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
captureCommitPhaseError(fiber, error);
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
fiber.effectTag &= ~PassiveUnmountPendingDev;
|
||||
const alternate = fiber.alternate;
|
||||
if (alternate !== null) {
|
||||
alternate.effectTag &= ~PassiveUnmountPendingDev;
|
||||
}
|
||||
}
|
||||
// Second pass: Create new passive effects.
|
||||
const mountEffects = pendingPassiveHookEffectsMount;
|
||||
pendingPassiveHookEffectsMount = [];
|
||||
for (let i = 0; i < mountEffects.length; i += 2) {
|
||||
const effect = ((mountEffects[i]: any): HookEffect);
|
||||
const fiber = ((mountEffects[i + 1]: any): Fiber);
|
||||
|
||||
if (typeof destroy === 'function') {
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
if (
|
||||
@@ -2557,10 +2497,10 @@ function flushPassiveEffectsImpl() {
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
startPassiveEffectTimer();
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
recordPassiveEffectDuration(fiber);
|
||||
} else {
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
invokeGuardedCallback(null, destroy, null);
|
||||
}
|
||||
if (hasCaughtError()) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
@@ -2570,7 +2510,6 @@ function flushPassiveEffectsImpl() {
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
const create = effect.create;
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
@@ -2578,12 +2517,12 @@ function flushPassiveEffectsImpl() {
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
effect.destroy = create();
|
||||
destroy();
|
||||
} finally {
|
||||
recordPassiveEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
effect.destroy = create();
|
||||
destroy();
|
||||
}
|
||||
} catch (error) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
@@ -2592,33 +2531,60 @@ function flushPassiveEffectsImpl() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Second pass: Create new passive effects.
|
||||
const mountEffects = pendingPassiveHookEffectsMount;
|
||||
pendingPassiveHookEffectsMount = [];
|
||||
for (let i = 0; i < mountEffects.length; i += 2) {
|
||||
const effect = ((mountEffects[i]: any): HookEffect);
|
||||
const fiber = ((mountEffects[i + 1]: any): Fiber);
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(fiber);
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
startPassiveEffectTimer();
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
recordPassiveEffectDuration(fiber);
|
||||
} else {
|
||||
invokeGuardedCallback(null, invokePassiveEffectCreate, null, effect);
|
||||
}
|
||||
if (hasCaughtError()) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
const error = clearCaughtError();
|
||||
captureCommitPhaseError(fiber, error);
|
||||
}
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
const create = effect.create;
|
||||
if (
|
||||
enableProfilerTimer &&
|
||||
enableProfilerCommitHooks &&
|
||||
fiber.mode & ProfileMode
|
||||
) {
|
||||
try {
|
||||
startPassiveEffectTimer();
|
||||
effect.destroy = create();
|
||||
} finally {
|
||||
recordPassiveEffectDuration(fiber);
|
||||
}
|
||||
} else {
|
||||
effect.destroy = create();
|
||||
}
|
||||
} catch (error) {
|
||||
invariant(fiber !== null, 'Should be working on an effect.');
|
||||
captureCommitPhaseError(fiber, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: This currently assumes there are no passive effects on the root fiber
|
||||
// because the root is not part of its own effect list.
|
||||
// This could change in the future.
|
||||
let effect = root.current.firstEffect;
|
||||
while (effect !== null) {
|
||||
// We do this work above if this flag is enabled, so we shouldn't be
|
||||
// doing it here.
|
||||
if (!runAllPassiveEffectDestroysBeforeCreates) {
|
||||
if (__DEV__) {
|
||||
setCurrentDebugFiberInDEV(effect);
|
||||
invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);
|
||||
if (hasCaughtError()) {
|
||||
invariant(effect !== null, 'Should be working on an effect.');
|
||||
const error = clearCaughtError();
|
||||
captureCommitPhaseError(effect, error);
|
||||
}
|
||||
resetCurrentDebugFiberInDEV();
|
||||
} else {
|
||||
try {
|
||||
commitPassiveHookEffects(effect);
|
||||
} catch (error) {
|
||||
invariant(effect !== null, 'Should be working on an effect.');
|
||||
captureCommitPhaseError(effect, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nextNextEffect = effect.nextEffect;
|
||||
// Remove nextEffect pointer to assist GC
|
||||
effect.nextEffect = null;
|
||||
@@ -3037,15 +3003,10 @@ function warnAboutUpdateOnUnmountedFiberInDEV(fiber) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
deferPassiveEffectCleanupDuringUnmount &&
|
||||
runAllPassiveEffectDestroysBeforeCreates
|
||||
) {
|
||||
// If there are pending passive effects unmounts for this Fiber,
|
||||
// we can assume that they would have prevented this update.
|
||||
if ((fiber.effectTag & PassiveUnmountPendingDev) !== NoEffect) {
|
||||
return;
|
||||
}
|
||||
// If there are pending passive effects unmounts for this Fiber,
|
||||
// we can assume that they would have prevented this update.
|
||||
if ((fiber.effectTag & PassiveUnmountPendingDev) !== NoEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We show the whole stack but dedupe on the top component's name because
|
||||
|
||||
+93
-149
@@ -15,7 +15,6 @@
|
||||
let React;
|
||||
let ReactCache;
|
||||
let TextResource;
|
||||
let ReactFeatureFlags;
|
||||
let ReactNoop;
|
||||
let Scheduler;
|
||||
let SchedulerTracing;
|
||||
@@ -33,21 +32,12 @@ let useDeferredValue;
|
||||
let forwardRef;
|
||||
let memo;
|
||||
let act;
|
||||
let deferPassiveEffectCleanupDuringUnmount;
|
||||
let runAllPassiveEffectDestroysBeforeCreates;
|
||||
|
||||
describe('ReactHooksWithNoopRenderer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.useFakeTimers();
|
||||
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
|
||||
deferPassiveEffectCleanupDuringUnmount =
|
||||
ReactFeatureFlags.deferPassiveEffectCleanupDuringUnmount;
|
||||
runAllPassiveEffectDestroysBeforeCreates =
|
||||
ReactFeatureFlags.runAllPassiveEffectDestroysBeforeCreates;
|
||||
|
||||
React = require('react');
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
Scheduler = require('scheduler');
|
||||
@@ -1119,7 +1109,6 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
},
|
||||
);
|
||||
|
||||
// @gate deferPassiveEffectCleanupDuringUnmount && runAllPassiveEffectDestroysBeforeCreates
|
||||
it('defers passive effect destroy functions during unmount', () => {
|
||||
function Child({bar, foo}) {
|
||||
React.useEffect(() => {
|
||||
@@ -1204,7 +1193,6 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// @gate deferPassiveEffectCleanupDuringUnmount && runAllPassiveEffectDestroysBeforeCreates
|
||||
it('does not warn about state updates for unmounted components with pending passive unmounts', () => {
|
||||
let completePendingRequest = null;
|
||||
function Component() {
|
||||
@@ -1252,7 +1240,6 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// @gate deferPassiveEffectCleanupDuringUnmount && runAllPassiveEffectDestroysBeforeCreates
|
||||
it('does not warn about state updates for unmounted components with pending passive unmounts for alternates', () => {
|
||||
let setParentState = null;
|
||||
const setChildStates = [];
|
||||
@@ -1378,7 +1365,6 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// @gate deferPassiveEffectCleanupDuringUnmount && runAllPassiveEffectDestroysBeforeCreates
|
||||
it('still warns if there are pending passive unmount effects but not for the current fiber', () => {
|
||||
let completePendingRequest = null;
|
||||
function ComponentWithXHR() {
|
||||
@@ -2085,80 +2071,66 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
if (runAllPassiveEffectDestroysBeforeCreates) {
|
||||
it('unmounts all previous effects between siblings before creating any new ones', () => {
|
||||
function Counter({count, label}) {
|
||||
useEffect(() => {
|
||||
Scheduler.unstable_yieldValue(`Mount ${label} [${count}]`);
|
||||
return () => {
|
||||
Scheduler.unstable_yieldValue(`Unmount ${label} [${count}]`);
|
||||
};
|
||||
});
|
||||
return <Text text={`${label} ${count}`} />;
|
||||
}
|
||||
act(() => {
|
||||
ReactNoop.render(
|
||||
<>
|
||||
<Counter label="A" count={0} />
|
||||
<Counter label="B" count={0} />
|
||||
</>,
|
||||
() => Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough([
|
||||
'A 0',
|
||||
'B 0',
|
||||
'Sync effect',
|
||||
]);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('A 0'), span('B 0')]);
|
||||
it('unmounts all previous effects between siblings before creating any new ones', () => {
|
||||
function Counter({count, label}) {
|
||||
useEffect(() => {
|
||||
Scheduler.unstable_yieldValue(`Mount ${label} [${count}]`);
|
||||
return () => {
|
||||
Scheduler.unstable_yieldValue(`Unmount ${label} [${count}]`);
|
||||
};
|
||||
});
|
||||
|
||||
expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
|
||||
|
||||
act(() => {
|
||||
ReactNoop.render(
|
||||
<>
|
||||
<Counter label="A" count={1} />
|
||||
<Counter label="B" count={1} />
|
||||
</>,
|
||||
() => Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough([
|
||||
'A 1',
|
||||
'B 1',
|
||||
'Sync effect',
|
||||
]);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('A 1'), span('B 1')]);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Unmount A [0]',
|
||||
'Unmount B [0]',
|
||||
'Mount A [1]',
|
||||
'Mount B [1]',
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
ReactNoop.render(
|
||||
<>
|
||||
<Counter label="B" count={2} />
|
||||
<Counter label="C" count={0} />
|
||||
</>,
|
||||
() => Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough([
|
||||
'B 2',
|
||||
'C 0',
|
||||
'Sync effect',
|
||||
]);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('B 2'), span('C 0')]);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Unmount A [1]',
|
||||
'Unmount B [1]',
|
||||
'Mount B [2]',
|
||||
'Mount C [0]',
|
||||
]);
|
||||
return <Text text={`${label} ${count}`} />;
|
||||
}
|
||||
act(() => {
|
||||
ReactNoop.render(
|
||||
<>
|
||||
<Counter label="A" count={0} />
|
||||
<Counter label="B" count={0} />
|
||||
</>,
|
||||
() => Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough(['A 0', 'B 0', 'Sync effect']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('A 0'), span('B 0')]);
|
||||
});
|
||||
}
|
||||
|
||||
expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
|
||||
|
||||
act(() => {
|
||||
ReactNoop.render(
|
||||
<>
|
||||
<Counter label="A" count={1} />
|
||||
<Counter label="B" count={1} />
|
||||
</>,
|
||||
() => Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough(['A 1', 'B 1', 'Sync effect']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('A 1'), span('B 1')]);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Unmount A [0]',
|
||||
'Unmount B [0]',
|
||||
'Mount A [1]',
|
||||
'Mount B [1]',
|
||||
]);
|
||||
|
||||
act(() => {
|
||||
ReactNoop.render(
|
||||
<>
|
||||
<Counter label="B" count={2} />
|
||||
<Counter label="C" count={0} />
|
||||
</>,
|
||||
() => Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough(['B 2', 'C 0', 'Sync effect']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('B 2'), span('C 0')]);
|
||||
});
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Unmount A [1]',
|
||||
'Unmount B [1]',
|
||||
'Mount B [2]',
|
||||
'Mount C [0]',
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles errors in create on mount', () => {
|
||||
function Counter(props) {
|
||||
@@ -2236,30 +2208,19 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
|
||||
expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
|
||||
expect(Scheduler).toHaveYielded(
|
||||
deferPassiveEffectCleanupDuringUnmount &&
|
||||
runAllPassiveEffectDestroysBeforeCreates
|
||||
? ['Unmount A [0]', 'Unmount B [0]', 'Mount A [1]', 'Oops!']
|
||||
: [
|
||||
'Unmount A [0]',
|
||||
'Unmount B [0]',
|
||||
'Mount A [1]',
|
||||
'Oops!',
|
||||
'Unmount A [1]',
|
||||
],
|
||||
);
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Unmount A [0]',
|
||||
'Unmount B [0]',
|
||||
'Mount A [1]',
|
||||
'Oops!',
|
||||
]);
|
||||
expect(ReactNoop.getChildren()).toEqual([]);
|
||||
});
|
||||
if (
|
||||
deferPassiveEffectCleanupDuringUnmount &&
|
||||
runAllPassiveEffectDestroysBeforeCreates
|
||||
) {
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// Clean up effect A runs passively on unmount.
|
||||
// There's no effect B to clean-up, because it never mounted.
|
||||
'Unmount A [1]',
|
||||
]);
|
||||
}
|
||||
expect(Scheduler).toHaveYielded([
|
||||
// Clean up effect A runs passively on unmount.
|
||||
// There's no effect B to clean-up, because it never mounted.
|
||||
'Unmount A [1]',
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles errors in destroy on update', () => {
|
||||
@@ -2292,49 +2253,32 @@ describe('ReactHooksWithNoopRenderer', () => {
|
||||
expect(Scheduler).toHaveYielded(['Mount A [0]', 'Mount B [0]']);
|
||||
});
|
||||
|
||||
if (
|
||||
deferPassiveEffectCleanupDuringUnmount &&
|
||||
runAllPassiveEffectDestroysBeforeCreates
|
||||
) {
|
||||
act(() => {
|
||||
// This update will trigger an error during passive effect unmount
|
||||
ReactNoop.render(<Counter count={1} />, () =>
|
||||
Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
|
||||
expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
|
||||
act(() => {
|
||||
// This update will trigger an error during passive effect unmount
|
||||
ReactNoop.render(<Counter count={1} />, () =>
|
||||
Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(Scheduler).toFlushAndYieldThrough(['Count: 1', 'Sync effect']);
|
||||
expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);
|
||||
expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');
|
||||
|
||||
// This branch enables a feature flag that flushes all passive destroys in a
|
||||
// separate pass before flushing any passive creates.
|
||||
// A result of this two-pass flush is that an error thrown from unmount does
|
||||
// not block the subsequent create functions from being run.
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Oops!',
|
||||
'Unmount B [0]',
|
||||
'Mount A [1]',
|
||||
'Mount B [1]',
|
||||
]);
|
||||
});
|
||||
// This branch enables a feature flag that flushes all passive destroys in a
|
||||
// separate pass before flushing any passive creates.
|
||||
// A result of this two-pass flush is that an error thrown from unmount does
|
||||
// not block the subsequent create functions from being run.
|
||||
expect(Scheduler).toHaveYielded([
|
||||
'Oops!',
|
||||
'Unmount B [0]',
|
||||
'Mount A [1]',
|
||||
'Mount B [1]',
|
||||
]);
|
||||
});
|
||||
|
||||
// <Counter> gets unmounted because an error is thrown above.
|
||||
// The remaining destroy functions are run later on unmount, since they're passive.
|
||||
// In this case, one of them throws again (because of how the test is written).
|
||||
expect(Scheduler).toHaveYielded(['Oops!', 'Unmount B [1]']);
|
||||
expect(ReactNoop.getChildren()).toEqual([]);
|
||||
} else {
|
||||
act(() => {
|
||||
// This update will trigger an error during passive effect unmount
|
||||
ReactNoop.render(<Counter count={1} />, () =>
|
||||
Scheduler.unstable_yieldValue('Sync effect'),
|
||||
);
|
||||
expect(() => {
|
||||
expect(Scheduler).toFlushAndYield(['Count: 1', 'Sync effect']);
|
||||
}).toThrow('Oops!');
|
||||
expect(ReactNoop.getChildren()).toEqual([]);
|
||||
ReactNoop.flushPassiveEffects();
|
||||
});
|
||||
}
|
||||
// <Counter> gets unmounted because an error is thrown above.
|
||||
// The remaining destroy functions are run later on unmount, since they're passive.
|
||||
// In this case, one of them throws again (because of how the test is written).
|
||||
expect(Scheduler).toHaveYielded(['Oops!', 'Unmount B [1]']);
|
||||
expect(ReactNoop.getChildren()).toEqual([]);
|
||||
});
|
||||
|
||||
it('works with memo', () => {
|
||||
|
||||
+16
-46
@@ -1,5 +1,4 @@
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
let Fragment;
|
||||
let ReactNoop;
|
||||
let Scheduler;
|
||||
@@ -14,7 +13,6 @@ describe('ReactSuspenseWithNoopRenderer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
React = require('react');
|
||||
Fragment = React.Fragment;
|
||||
ReactNoop = require('react-noop-renderer');
|
||||
@@ -1737,26 +1735,13 @@ describe('ReactSuspenseWithNoopRenderer', () => {
|
||||
|
||||
expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
|
||||
|
||||
if (
|
||||
ReactFeatureFlags.deferPassiveEffectCleanupDuringUnmount &&
|
||||
ReactFeatureFlags.runAllPassiveEffectDestroysBeforeCreates
|
||||
) {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'B',
|
||||
'Destroy Layout Effect [Loading...]',
|
||||
'Layout Effect [B]',
|
||||
'Destroy Effect [Loading...]',
|
||||
'Effect [B]',
|
||||
]);
|
||||
} else {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'B',
|
||||
'Destroy Layout Effect [Loading...]',
|
||||
'Destroy Effect [Loading...]',
|
||||
'Layout Effect [B]',
|
||||
'Effect [B]',
|
||||
]);
|
||||
}
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'B',
|
||||
'Destroy Layout Effect [Loading...]',
|
||||
'Layout Effect [B]',
|
||||
'Destroy Effect [Loading...]',
|
||||
'Effect [B]',
|
||||
]);
|
||||
|
||||
// Update
|
||||
ReactNoop.renderLegacySyncRoot(<App text="B2" />, () =>
|
||||
@@ -1786,30 +1771,15 @@ describe('ReactSuspenseWithNoopRenderer', () => {
|
||||
|
||||
expect(Scheduler).toHaveYielded(['Promise resolved [B2]']);
|
||||
|
||||
if (
|
||||
ReactFeatureFlags.deferPassiveEffectCleanupDuringUnmount &&
|
||||
ReactFeatureFlags.runAllPassiveEffectDestroysBeforeCreates
|
||||
) {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'B2',
|
||||
'Destroy Layout Effect [Loading...]',
|
||||
'Destroy Layout Effect [B]',
|
||||
'Layout Effect [B2]',
|
||||
'Destroy Effect [Loading...]',
|
||||
'Destroy Effect [B]',
|
||||
'Effect [B2]',
|
||||
]);
|
||||
} else {
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'B2',
|
||||
'Destroy Layout Effect [Loading...]',
|
||||
'Destroy Effect [Loading...]',
|
||||
'Destroy Layout Effect [B]',
|
||||
'Layout Effect [B2]',
|
||||
'Destroy Effect [B]',
|
||||
'Effect [B2]',
|
||||
]);
|
||||
}
|
||||
expect(Scheduler).toFlushAndYield([
|
||||
'B2',
|
||||
'Destroy Layout Effect [Loading...]',
|
||||
'Destroy Layout Effect [B]',
|
||||
'Layout Effect [B2]',
|
||||
'Destroy Effect [Loading...]',
|
||||
'Destroy Effect [B]',
|
||||
'Effect [B2]',
|
||||
]);
|
||||
});
|
||||
|
||||
it('suspends for longer if something took a long (CPU bound) time to render', async () => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -82,21 +82,6 @@ export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
||||
|
||||
export const enableTrustedTypesIntegration = false;
|
||||
|
||||
// Controls sequence of passive effect destroy and create functions.
|
||||
// If this flag is off, destroy and create functions may be interleaved.
|
||||
// When the flag is on, all destroy functions will be run (for all fibers)
|
||||
// before any create functions are run, similar to how layout effects work.
|
||||
// This flag provides a killswitch if that proves to break existing code somehow.
|
||||
export const runAllPassiveEffectDestroysBeforeCreates = false;
|
||||
|
||||
// Controls behavior of deferred effect destroy functions during unmount.
|
||||
// Previously these functions were run during commit (along with layout effects).
|
||||
// Ideally we should delay these until after commit for performance reasons.
|
||||
// This flag provides a killswitch if that proves to break existing code somehow.
|
||||
//
|
||||
// WARNING This flag only has an affect if used with runAllPassiveEffectDestroysBeforeCreates.
|
||||
export const deferPassiveEffectCleanupDuringUnmount = false;
|
||||
|
||||
// Enables a warning when trying to spread a 'key' to an element;
|
||||
// a deprecated pattern we want to get rid of in the future
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
|
||||
@@ -38,8 +38,6 @@ export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnUnstableRenderSubtreeIntoContainer = false;
|
||||
export const deferPassiveEffectCleanupDuringUnmount = false;
|
||||
export const runAllPassiveEffectDestroysBeforeCreates = false;
|
||||
export const enableModernEventSystem = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const enableComponentStackLocations = false;
|
||||
|
||||
@@ -37,8 +37,6 @@ export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnUnstableRenderSubtreeIntoContainer = false;
|
||||
export const deferPassiveEffectCleanupDuringUnmount = false;
|
||||
export const runAllPassiveEffectDestroysBeforeCreates = false;
|
||||
export const enableModernEventSystem = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const enableComponentStackLocations = false;
|
||||
|
||||
@@ -37,8 +37,6 @@ export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnUnstableRenderSubtreeIntoContainer = false;
|
||||
export const deferPassiveEffectCleanupDuringUnmount = false;
|
||||
export const runAllPassiveEffectDestroysBeforeCreates = false;
|
||||
export const enableModernEventSystem = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const enableComponentStackLocations = false;
|
||||
|
||||
@@ -37,8 +37,6 @@ export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = true;
|
||||
export const warnUnstableRenderSubtreeIntoContainer = false;
|
||||
export const deferPassiveEffectCleanupDuringUnmount = true;
|
||||
export const runAllPassiveEffectDestroysBeforeCreates = true;
|
||||
export const enableModernEventSystem = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const enableComponentStackLocations = false;
|
||||
|
||||
@@ -37,8 +37,6 @@ export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = false;
|
||||
export const disableModulePatternComponents = false;
|
||||
export const warnUnstableRenderSubtreeIntoContainer = false;
|
||||
export const deferPassiveEffectCleanupDuringUnmount = false;
|
||||
export const runAllPassiveEffectDestroysBeforeCreates = false;
|
||||
export const enableModernEventSystem = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const enableComponentStackLocations = false;
|
||||
|
||||
@@ -37,8 +37,6 @@ export const enableTrustedTypesIntegration = false;
|
||||
export const disableTextareaChildren = __EXPERIMENTAL__;
|
||||
export const disableModulePatternComponents = true;
|
||||
export const warnUnstableRenderSubtreeIntoContainer = false;
|
||||
export const deferPassiveEffectCleanupDuringUnmount = true;
|
||||
export const runAllPassiveEffectDestroysBeforeCreates = true;
|
||||
export const enableModernEventSystem = false;
|
||||
export const warnAboutSpreadingKeyToJSX = false;
|
||||
export const enableComponentStackLocations = false;
|
||||
|
||||
@@ -76,9 +76,6 @@ export const warnUnstableRenderSubtreeIntoContainer = false;
|
||||
// to the correct value.
|
||||
export const enableNewReconciler = __VARIANT__;
|
||||
|
||||
export const deferPassiveEffectCleanupDuringUnmount = true;
|
||||
export const runAllPassiveEffectDestroysBeforeCreates = true;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
type Check<_X, Y: _X, X: Y = _X> = null;
|
||||
|
||||
Reference in New Issue
Block a user