Skip to content

Commit 322042c

Browse files
JeanMecheatscott
authored andcommitted
fix(core): destroying the effect on afterRenderEffect (#63001)
Prior to this commit, the effect node wasn't destroyed. fixes #62980 PR Close #63001
1 parent 4aa120a commit 322042c

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

packages/core/src/render3/reactivity/after_render_effect.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {
1010
consumerAfterComputation,
1111
consumerBeforeComputation,
12+
consumerDestroy,
1213
consumerPollProducersForChange,
1314
producerAccessed,
1415
SIGNAL,
@@ -152,7 +153,7 @@ const AFTER_RENDER_PHASE_EFFECT_NODE = /* @__PURE__ */ (() => ({
152153
/**
153154
* An `AfterRenderSequence` that manages an `afterRenderEffect`'s phase effects.
154155
*/
155-
class AfterRenderEffectSequence extends AfterRenderSequence {
156+
export class AfterRenderEffectSequence extends AfterRenderSequence {
156157
/**
157158
* While this sequence is executing, this tracks the last phase which was called by the
158159
* `afterRender` machinery.
@@ -223,8 +224,14 @@ class AfterRenderEffectSequence extends AfterRenderSequence {
223224

224225
// Run the cleanup functions for each node.
225226
for (const node of this.nodes) {
226-
for (const fn of node?.cleanup ?? EMPTY_CLEANUP_SET) {
227-
fn();
227+
if (node) {
228+
try {
229+
for (const fn of node.cleanup ?? EMPTY_CLEANUP_SET) {
230+
fn();
231+
}
232+
} finally {
233+
consumerDestroy(node);
234+
}
228235
}
229236
}
230237
}

packages/core/test/acceptance/after_render_effect_spec.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import {
1414
provideZonelessChangeDetection,
1515
signal,
1616
} from '../../src/core';
17-
import {afterRenderEffect} from '../../src/render3/reactivity/after_render_effect';
17+
import {
18+
afterRenderEffect,
19+
AfterRenderEffectSequence,
20+
} from '../../src/render3/reactivity/after_render_effect';
21+
import {AfterRenderPhase} from '../../src/render3/after_render/api';
1822
import {TestBed} from '../../testing';
1923

2024
describe('afterRenderEffect', () => {
@@ -191,6 +195,37 @@ describe('afterRenderEffect', () => {
191195
expect(log).toEqual(['cleanup earlyRead', 'earlyRead: 4', 'cleanup write', 'write: true']);
192196
});
193197

198+
it('should disconnect the consummer from the graph when destroyed', () => {
199+
const log: string[] = [];
200+
const appRef = TestBed.inject(ApplicationRef);
201+
const counter = signal(0);
202+
203+
const ref = afterRenderEffect(
204+
{
205+
earlyRead: () => counter() % 2 === 0,
206+
write: (isEven) => isEven(),
207+
mixedReadWrite: (isEven) => isEven(),
208+
read: (isEven) => isEven(),
209+
},
210+
{injector: appRef.injector},
211+
) as AfterRenderEffectSequence;
212+
213+
appRef.tick();
214+
215+
const phaseNodes = ref['nodes'];
216+
expect(phaseNodes[AfterRenderPhase.EarlyRead]?.consumers).toBeDefined();
217+
expect(phaseNodes[AfterRenderPhase.Write]?.consumers).toBeDefined();
218+
expect(phaseNodes[AfterRenderPhase.MixedReadWrite]?.consumers).toBeDefined();
219+
expect(phaseNodes[AfterRenderPhase.Read]?.producers).toBeDefined();
220+
221+
ref.destroy();
222+
223+
expect(phaseNodes[AfterRenderPhase.EarlyRead]?.consumers).toBeUndefined();
224+
expect(phaseNodes[AfterRenderPhase.Write]?.consumers).toBeUndefined();
225+
expect(phaseNodes[AfterRenderPhase.MixedReadWrite]?.consumers).toBeUndefined();
226+
expect(phaseNodes[AfterRenderPhase.Read]?.producers).toBeUndefined();
227+
});
228+
194229
it('should run cleanup functions when destroyed', () => {
195230
const log: string[] = [];
196231
const appRef = TestBed.inject(ApplicationRef);

0 commit comments

Comments
 (0)