Skip to content

Commit 4e14498

Browse files
committed
offscreen double invoke effects
1 parent 5f1890f commit 4e14498

15 files changed

+454
-100
lines changed

packages/react-reconciler/src/ReactFiberCommitWork.new.js

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
enableFundamentalAPI,
3636
enableSuspenseCallback,
3737
enableScopeAPI,
38+
enableDoubleInvokingEffects,
3839
} from 'shared/ReactFeatureFlags';
3940
import {
4041
FunctionComponent,
@@ -313,7 +314,8 @@ function commitBeforeMutationLifeCycles(
313314
);
314315
}
315316

316-
function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
317+
function commitHookLayoutEffectListUnmount(finishedWork: Fiber) {
318+
const tag = HookLayout | HookHasEffect;
317319
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
318320
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
319321
if (lastEffect !== null) {
@@ -325,15 +327,29 @@ function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
325327
const destroy = effect.destroy;
326328
effect.destroy = undefined;
327329
if (destroy !== undefined) {
328-
destroy();
330+
if (
331+
enableProfilerTimer &&
332+
enableProfilerCommitHooks &&
333+
finishedWork.mode & ProfileMode
334+
) {
335+
try {
336+
startLayoutEffectTimer();
337+
destroy();
338+
} finally {
339+
recordLayoutEffectDuration(finishedWork);
340+
}
341+
} else {
342+
destroy();
343+
}
329344
}
330345
}
331346
effect = effect.next;
332347
} while (effect !== firstEffect);
333348
}
334349
}
335350

336-
function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
351+
function commitHookLayoutEffectListMount(finishedWork: Fiber) {
352+
const tag = HookLayout | HookHasEffect;
337353
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
338354
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
339355
if (lastEffect !== null) {
@@ -343,8 +359,20 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
343359
if ((effect.tag & tag) === tag) {
344360
// Mount
345361
const create = effect.create;
346-
effect.destroy = create();
347-
362+
if (
363+
enableProfilerTimer &&
364+
enableProfilerCommitHooks &&
365+
finishedWork.mode & ProfileMode
366+
) {
367+
try {
368+
startLayoutEffectTimer();
369+
effect.destroy = create();
370+
} finally {
371+
recordLayoutEffectDuration(finishedWork);
372+
}
373+
} else {
374+
effect.destroy = create();
375+
}
348376
if (__DEV__) {
349377
const destroy = effect.destroy;
350378
if (destroy !== undefined && typeof destroy !== 'function') {
@@ -376,6 +404,14 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
376404
addendum,
377405
);
378406
}
407+
408+
if (enableDoubleInvokingEffects) {
409+
effect.destroy = undefined;
410+
if (typeof destroy === 'function') {
411+
destroy();
412+
}
413+
effect.destroy = create();
414+
}
379415
}
380416
}
381417
effect = effect.next;
@@ -453,20 +489,7 @@ function commitLifeCycles(
453489
// This is done to prevent sibling component effects from interfering with each other,
454490
// e.g. a destroy function in one component should never override a ref set
455491
// by a create function in another component during the same commit.
456-
if (
457-
enableProfilerTimer &&
458-
enableProfilerCommitHooks &&
459-
finishedWork.mode & ProfileMode
460-
) {
461-
try {
462-
startLayoutEffectTimer();
463-
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
464-
} finally {
465-
recordLayoutEffectDuration(finishedWork);
466-
}
467-
} else {
468-
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
469-
}
492+
commitHookLayoutEffectListMount(finishedWork);
470493

471494
if ((finishedWork.subtreeTag & PassiveSubtreeTag) !== NoSubtreeTag) {
472495
schedulePassiveEffectCallback();
@@ -521,6 +544,16 @@ function commitLifeCycles(
521544
} else {
522545
instance.componentDidMount();
523546
}
547+
548+
if (__DEV__) {
549+
if (enableDoubleInvokingEffects) {
550+
// if there is no unmount function we don't have to double invoke
551+
if (typeof instance.componentWillUnmount === 'function') {
552+
safelyCallComponentWillUnmount(finishedWork, instance);
553+
}
554+
instance.componentDidMount();
555+
}
556+
}
524557
} else {
525558
const prevProps =
526559
finishedWork.elementType === finishedWork.type
@@ -1428,23 +1461,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
14281461
// This prevents sibling component effects from interfering with each other,
14291462
// e.g. a destroy function in one component should never override a ref set
14301463
// by a create function in another component during the same commit.
1431-
if (
1432-
enableProfilerTimer &&
1433-
enableProfilerCommitHooks &&
1434-
finishedWork.mode & ProfileMode
1435-
) {
1436-
try {
1437-
startLayoutEffectTimer();
1438-
commitHookEffectListUnmount(
1439-
HookLayout | HookHasEffect,
1440-
finishedWork,
1441-
);
1442-
} finally {
1443-
recordLayoutEffectDuration(finishedWork);
1444-
}
1445-
} else {
1446-
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
1447-
}
1464+
commitHookLayoutEffectListUnmount(finishedWork);
14481465
return;
14491466
}
14501467
case Profiler: {
@@ -1491,20 +1508,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
14911508
// This prevents sibling component effects from interfering with each other,
14921509
// e.g. a destroy function in one component should never override a ref set
14931510
// by a create function in another component during the same commit.
1494-
if (
1495-
enableProfilerTimer &&
1496-
enableProfilerCommitHooks &&
1497-
finishedWork.mode & ProfileMode
1498-
) {
1499-
try {
1500-
startLayoutEffectTimer();
1501-
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
1502-
} finally {
1503-
recordLayoutEffectDuration(finishedWork);
1504-
}
1505-
} else {
1506-
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
1507-
}
1511+
commitHookLayoutEffectListUnmount(finishedWork);
15081512
return;
15091513
}
15101514
case ClassComponent: {

packages/react-reconciler/src/ReactFiberCommitWork.old.js

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
enableFundamentalAPI,
3636
enableSuspenseCallback,
3737
enableScopeAPI,
38+
enableDoubleInvokingEffects,
3839
} from 'shared/ReactFeatureFlags';
3940
import {
4041
FunctionComponent,
@@ -311,7 +312,8 @@ function commitBeforeMutationLifeCycles(
311312
);
312313
}
313314

314-
function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
315+
function commitHookLayoutEffectListUnmount(finishedWork: Fiber) {
316+
const tag = HookLayout | HookHasEffect;
315317
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
316318
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
317319
if (lastEffect !== null) {
@@ -323,15 +325,29 @@ function commitHookEffectListUnmount(tag: number, finishedWork: Fiber) {
323325
const destroy = effect.destroy;
324326
effect.destroy = undefined;
325327
if (destroy !== undefined) {
326-
destroy();
328+
if (
329+
enableProfilerTimer &&
330+
enableProfilerCommitHooks &&
331+
finishedWork.mode & ProfileMode
332+
) {
333+
try {
334+
startLayoutEffectTimer();
335+
destroy();
336+
} finally {
337+
recordLayoutEffectDuration(finishedWork);
338+
}
339+
} else {
340+
destroy();
341+
}
327342
}
328343
}
329344
effect = effect.next;
330345
} while (effect !== firstEffect);
331346
}
332347
}
333348

334-
function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
349+
function commitHookLayoutEffectListMount(finishedWork: Fiber) {
350+
const tag = HookLayout | HookHasEffect;
335351
const updateQueue: FunctionComponentUpdateQueue | null = (finishedWork.updateQueue: any);
336352
const lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
337353
if (lastEffect !== null) {
@@ -341,8 +357,20 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
341357
if ((effect.tag & tag) === tag) {
342358
// Mount
343359
const create = effect.create;
344-
effect.destroy = create();
345-
360+
if (
361+
enableProfilerTimer &&
362+
enableProfilerCommitHooks &&
363+
finishedWork.mode & ProfileMode
364+
) {
365+
try {
366+
startLayoutEffectTimer();
367+
effect.destroy = create();
368+
} finally {
369+
recordLayoutEffectDuration(finishedWork);
370+
}
371+
} else {
372+
effect.destroy = create();
373+
}
346374
if (__DEV__) {
347375
const destroy = effect.destroy;
348376
if (destroy !== undefined && typeof destroy !== 'function') {
@@ -374,6 +402,14 @@ function commitHookEffectListMount(tag: number, finishedWork: Fiber) {
374402
addendum,
375403
);
376404
}
405+
406+
if (enableDoubleInvokingEffects) {
407+
effect.destroy = undefined;
408+
if (typeof destroy === 'function') {
409+
destroy();
410+
}
411+
effect.destroy = create();
412+
}
377413
}
378414
}
379415
effect = effect.next;
@@ -471,20 +507,7 @@ function commitLifeCycles(
471507
// This is done to prevent sibling component effects from interfering with each other,
472508
// e.g. a destroy function in one component should never override a ref set
473509
// by a create function in another component during the same commit.
474-
if (
475-
enableProfilerTimer &&
476-
enableProfilerCommitHooks &&
477-
finishedWork.mode & ProfileMode
478-
) {
479-
try {
480-
startLayoutEffectTimer();
481-
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
482-
} finally {
483-
recordLayoutEffectDuration(finishedWork);
484-
}
485-
} else {
486-
commitHookEffectListMount(HookLayout | HookHasEffect, finishedWork);
487-
}
510+
commitHookLayoutEffectListMount(finishedWork);
488511

489512
schedulePassiveEffects(finishedWork);
490513
return;
@@ -537,6 +560,16 @@ function commitLifeCycles(
537560
} else {
538561
instance.componentDidMount();
539562
}
563+
564+
if (__DEV__) {
565+
if (enableDoubleInvokingEffects) {
566+
// if there is no unmount function we don't have to double invoke
567+
if (typeof instance.componentWillUnmount === 'function') {
568+
safelyCallComponentWillUnmount(finishedWork, instance);
569+
}
570+
instance.componentDidMount();
571+
}
572+
}
540573
} else {
541574
const prevProps =
542575
finishedWork.elementType === finishedWork.type
@@ -1451,23 +1484,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
14511484
// This prevents sibling component effects from interfering with each other,
14521485
// e.g. a destroy function in one component should never override a ref set
14531486
// by a create function in another component during the same commit.
1454-
if (
1455-
enableProfilerTimer &&
1456-
enableProfilerCommitHooks &&
1457-
finishedWork.mode & ProfileMode
1458-
) {
1459-
try {
1460-
startLayoutEffectTimer();
1461-
commitHookEffectListUnmount(
1462-
HookLayout | HookHasEffect,
1463-
finishedWork,
1464-
);
1465-
} finally {
1466-
recordLayoutEffectDuration(finishedWork);
1467-
}
1468-
} else {
1469-
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
1470-
}
1487+
commitHookLayoutEffectListUnmount(finishedWork);
14711488
return;
14721489
}
14731490
case Profiler: {
@@ -1514,20 +1531,7 @@ function commitWork(current: Fiber | null, finishedWork: Fiber): void {
15141531
// This prevents sibling component effects from interfering with each other,
15151532
// e.g. a destroy function in one component should never override a ref set
15161533
// by a create function in another component during the same commit.
1517-
if (
1518-
enableProfilerTimer &&
1519-
enableProfilerCommitHooks &&
1520-
finishedWork.mode & ProfileMode
1521-
) {
1522-
try {
1523-
startLayoutEffectTimer();
1524-
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
1525-
} finally {
1526-
recordLayoutEffectDuration(finishedWork);
1527-
}
1528-
} else {
1529-
commitHookEffectListUnmount(HookLayout | HookHasEffect, finishedWork);
1530-
}
1534+
commitHookLayoutEffectListUnmount(finishedWork);
15311535
return;
15321536
}
15331537
case ClassComponent: {

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
enableDebugTracing,
3333
enableSchedulingProfiler,
3434
enableScopeAPI,
35+
enableDoubleInvokingEffects,
3536
} from 'shared/ReactFeatureFlags';
3637
import ReactSharedInternals from 'shared/ReactSharedInternals';
3738
import invariant from 'shared/invariant';
@@ -2686,6 +2687,16 @@ function invokePassiveEffectCreate(effect: HookEffect): void {
26862687
effect.destroy = create();
26872688
}
26882689

2690+
function doubleInvokePassiveEffect(effect: HookEffect): void {
2691+
const create = effect.create;
2692+
const destroy = effect.destroy;
2693+
effect.destroy = undefined;
2694+
if (typeof destroy === 'function') {
2695+
destroy();
2696+
}
2697+
effect.destroy = create();
2698+
}
2699+
26892700
function flushPassiveMountEffects(firstChild: Fiber): void {
26902701
let fiber = firstChild;
26912702
while (fiber !== null) {
@@ -2747,6 +2758,16 @@ function flushPassiveMountEffectsImpl(fiber: Fiber): void {
27472758
effect,
27482759
);
27492760
}
2761+
2762+
if (enableDoubleInvokingEffects) {
2763+
invokeGuardedCallback(
2764+
null,
2765+
doubleInvokePassiveEffect,
2766+
null,
2767+
effect,
2768+
);
2769+
}
2770+
27502771
if (hasCaughtError()) {
27512772
invariant(fiber !== null, 'Should be working on an effect.');
27522773
const error = clearCaughtError();

0 commit comments

Comments
 (0)