Skip to content

Commit d41e5c2

Browse files
committed
Revert "Fix: Move destroy field to shared instance object (#26561)"
This reverts commit 85bb7b6.
1 parent 481793a commit d41e5c2

3 files changed

Lines changed: 18 additions & 125 deletions

File tree

packages/react-reconciler/src/ReactFiberCommitWork.js

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -592,10 +592,9 @@ function commitHookEffectListUnmount(
592592
do {
593593
if ((effect.tag & flags) === flags) {
594594
// Unmount
595-
const inst = effect.inst;
596-
const destroy = inst.destroy;
595+
const destroy = effect.destroy;
596+
effect.destroy = undefined;
597597
if (destroy !== undefined) {
598-
inst.destroy = undefined;
599598
if (enableSchedulingProfiler) {
600599
if ((flags & HookPassive) !== NoHookEffect) {
601600
markComponentPassiveEffectUnmountStarted(finishedWork);
@@ -654,9 +653,7 @@ function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
654653
setIsRunningInsertionEffect(true);
655654
}
656655
}
657-
const inst = effect.inst;
658-
const destroy = create();
659-
inst.destroy = destroy;
656+
effect.destroy = create();
660657
if (__DEV__) {
661658
if ((flags & HookInsertion) !== NoHookEffect) {
662659
setIsRunningInsertionEffect(false);
@@ -672,6 +669,7 @@ function commitHookEffectListMount(flags: HookFlags, finishedWork: Fiber) {
672669
}
673670

674671
if (__DEV__) {
672+
const destroy = effect.destroy;
675673
if (destroy !== undefined && typeof destroy !== 'function') {
676674
let hookName;
677675
if ((effect.tag & HookLayout) !== NoFlags) {
@@ -2190,12 +2188,9 @@ function commitDeletionEffectsOnFiber(
21902188

21912189
let effect = firstEffect;
21922190
do {
2193-
const tag = effect.tag;
2194-
const inst = effect.inst;
2195-
const destroy = inst.destroy;
2191+
const {destroy, tag} = effect;
21962192
if (destroy !== undefined) {
21972193
if ((tag & HookInsertion) !== NoHookEffect) {
2198-
inst.destroy = undefined;
21992194
safelyCallDestroy(
22002195
deletedFiber,
22012196
nearestMountedAncestor,
@@ -2208,15 +2203,13 @@ function commitDeletionEffectsOnFiber(
22082203

22092204
if (shouldProfile(deletedFiber)) {
22102205
startLayoutEffectTimer();
2211-
inst.destroy = undefined;
22122206
safelyCallDestroy(
22132207
deletedFiber,
22142208
nearestMountedAncestor,
22152209
destroy,
22162210
);
22172211
recordLayoutEffectDuration(deletedFiber);
22182212
} else {
2219-
inst.destroy = undefined;
22202213
safelyCallDestroy(
22212214
deletedFiber,
22222215
nearestMountedAncestor,

packages/react-reconciler/src/ReactFiberHooks.js

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -180,29 +180,11 @@ export type Hook = {
180180
next: Hook | null,
181181
};
182182

183-
// The effect "instance" is a shared object that remains the same for the entire
184-
// lifetime of an effect. In Rust terms, a RefCell. We use it to store the
185-
// "destroy" function that is returned from an effect, because that is stateful.
186-
// The field is `undefined` if the effect is unmounted, or if the effect ran
187-
// but is not stateful. We don't explicitly track whether the effect is mounted
188-
// or unmounted because that can be inferred by the hiddenness of the fiber in
189-
// the tree, i.e. whether there is a hidden Offscreen fiber above it.
190-
//
191-
// It's unfortunate that this is stored on a separate object, because it adds
192-
// more memory per effect instance, but it's conceptually sound. I think there's
193-
// likely a better data structure we could use for effects; perhaps just one
194-
// array of effect instances per fiber. But I think this is OK for now despite
195-
// the additional memory and we can follow up with performance
196-
// optimizations later.
197-
type EffectInstance = {
198-
destroy: void | (() => void),
199-
};
200-
201183
export type Effect = {
202184
tag: HookFlags,
203185
create: () => (() => void) | void,
204-
inst: EffectInstance,
205-
deps: Array<mixed> | null,
186+
destroy: (() => void) | void,
187+
deps: Array<mixed> | void | null,
206188
next: Effect,
207189
};
208190

@@ -1680,7 +1662,7 @@ function mountSyncExternalStore<T>(
16801662
pushEffect(
16811663
HookHasEffect | HookPassive,
16821664
updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
1683-
createEffectInstance(),
1665+
undefined,
16841666
null,
16851667
);
16861668

@@ -1737,7 +1719,7 @@ function updateSyncExternalStore<T>(
17371719
pushEffect(
17381720
HookHasEffect | HookPassive,
17391721
updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
1740-
createEffectInstance(),
1722+
undefined,
17411723
null,
17421724
);
17431725

@@ -1878,13 +1860,13 @@ function rerenderState<S>(
18781860
function pushEffect(
18791861
tag: HookFlags,
18801862
create: () => (() => void) | void,
1881-
inst: EffectInstance,
1882-
deps: Array<mixed> | null,
1863+
destroy: (() => void) | void,
1864+
deps: Array<mixed> | void | null,
18831865
): Effect {
18841866
const effect: Effect = {
18851867
tag,
18861868
create,
1887-
inst,
1869+
destroy,
18881870
deps,
18891871
// Circular
18901872
next: (null: any),
@@ -1909,10 +1891,6 @@ function pushEffect(
19091891
return effect;
19101892
}
19111893

1912-
function createEffectInstance(): EffectInstance {
1913-
return {destroy: undefined};
1914-
}
1915-
19161894
let stackContainsErrorMessage: boolean | null = null;
19171895

19181896
function getCallerStackFrame(): string {
@@ -2016,7 +1994,7 @@ function mountEffectImpl(
20161994
hook.memoizedState = pushEffect(
20171995
HookHasEffect | hookFlags,
20181996
create,
2019-
createEffectInstance(),
1997+
undefined,
20201998
nextDeps,
20211999
);
20222000
}
@@ -2029,16 +2007,16 @@ function updateEffectImpl(
20292007
): void {
20302008
const hook = updateWorkInProgressHook();
20312009
const nextDeps = deps === undefined ? null : deps;
2032-
const effect: Effect = hook.memoizedState;
2033-
const inst = effect.inst;
2010+
let destroy = undefined;
20342011

20352012
// currentHook is null when rerendering after a render phase state update.
20362013
if (currentHook !== null) {
2014+
const prevEffect = currentHook.memoizedState;
2015+
destroy = prevEffect.destroy;
20372016
if (nextDeps !== null) {
2038-
const prevEffect: Effect = currentHook.memoizedState;
20392017
const prevDeps = prevEffect.deps;
20402018
if (areHookInputsEqual(nextDeps, prevDeps)) {
2041-
hook.memoizedState = pushEffect(hookFlags, create, inst, nextDeps);
2019+
hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
20422020
return;
20432021
}
20442022
}
@@ -2049,7 +2027,7 @@ function updateEffectImpl(
20492027
hook.memoizedState = pushEffect(
20502028
HookHasEffect | hookFlags,
20512029
create,
2052-
inst,
2030+
destroy,
20532031
nextDeps,
20542032
);
20552033
}

packages/react-reconciler/src/__tests__/ReactSuspenseEffectsSemanticsDOM-test.js

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -496,82 +496,4 @@ describe('ReactSuspenseEffectsSemanticsDOM', () => {
496496
ReactDOM.render(null, container);
497497
assertLog(['Unmount']);
498498
});
499-
500-
it('does not call cleanup effects twice after a bailout', async () => {
501-
const never = new Promise(resolve => {});
502-
function Never() {
503-
throw never;
504-
}
505-
506-
let setSuspended;
507-
let setLetter;
508-
509-
function App() {
510-
const [suspended, _setSuspended] = React.useState(false);
511-
setSuspended = _setSuspended;
512-
const [letter, _setLetter] = React.useState('A');
513-
setLetter = _setLetter;
514-
515-
return (
516-
<React.Suspense fallback="Loading...">
517-
<Child letter={letter} />
518-
{suspended && <Never />}
519-
</React.Suspense>
520-
);
521-
}
522-
523-
let nextId = 0;
524-
const freed = new Set();
525-
let setStep;
526-
527-
function Child({letter}) {
528-
const [, _setStep] = React.useState(0);
529-
setStep = _setStep;
530-
531-
React.useLayoutEffect(() => {
532-
const localId = nextId++;
533-
Scheduler.log('Did mount: ' + letter + localId);
534-
return () => {
535-
if (freed.has(localId)) {
536-
throw Error('Double free: ' + letter + localId);
537-
}
538-
freed.add(localId);
539-
Scheduler.log('Will unmount: ' + letter + localId);
540-
};
541-
}, [letter]);
542-
}
543-
544-
const root = ReactDOMClient.createRoot(container);
545-
await act(() => {
546-
root.render(<App />);
547-
});
548-
assertLog(['Did mount: A0']);
549-
550-
await act(() => {
551-
setStep(1);
552-
setSuspended(false);
553-
});
554-
assertLog([]);
555-
556-
await act(() => {
557-
setStep(1);
558-
});
559-
assertLog([]);
560-
561-
await act(() => {
562-
setSuspended(true);
563-
});
564-
assertLog(['Will unmount: A0']);
565-
566-
await act(() => {
567-
setSuspended(false);
568-
setLetter('B');
569-
});
570-
assertLog(['Did mount: B1']);
571-
572-
await act(() => {
573-
root.unmount();
574-
});
575-
assertLog(['Will unmount: B1']);
576-
});
577499
});

0 commit comments

Comments
 (0)