Skip to content

Commit fe03aa0

Browse files
JeanMecheatscott
authored andcommitted
Revert "fix(core): call DestroyRef on destroy callback if view is destroyed (angular#58008)" (angular#61625)
This reverts commit 5f7f046. PR Close angular#61625
1 parent c77e198 commit fe03aa0

File tree

3 files changed

+26
-62
lines changed

3 files changed

+26
-62
lines changed

packages/core/rxjs-interop/test/take_until_destroyed_spec.ts

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,8 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {
10-
Component,
11-
DestroyRef,
12-
EnvironmentInjector,
13-
inject,
14-
Injector,
15-
OnDestroy,
16-
runInInjectionContext,
17-
} from '../../src/core';
18-
import {TestBed} from '../../testing';
19-
import {BehaviorSubject, finalize} from 'rxjs';
9+
import {DestroyRef, EnvironmentInjector, Injector, runInInjectionContext} from '../../src/core';
10+
import {BehaviorSubject} from 'rxjs';
2011

2112
import {takeUntilDestroyed} from '../src/take_until_destroyed';
2213

@@ -85,39 +76,4 @@ describe('takeUntilDestroyed', () => {
8576

8677
expect(unregisterFn).toHaveBeenCalled();
8778
});
88-
89-
// https://github.com/angular/angular/issues/54527
90-
it('should unsubscribe after the current context has already been destroyed', async () => {
91-
const recorder: any[] = [];
92-
93-
// Note that we need a "real" view for this test because, in other cases,
94-
// `DestroyRef` would resolve to the root injector rather than to the
95-
// `NodeInjectorDestroyRef`, where `lView` is used.
96-
@Component({template: ''})
97-
class TestComponent implements OnDestroy {
98-
destroyRef = inject(DestroyRef);
99-
100-
source$ = new BehaviorSubject(0);
101-
102-
ngOnDestroy(): void {
103-
Promise.resolve().then(() => {
104-
this.source$
105-
.pipe(
106-
takeUntilDestroyed(this.destroyRef),
107-
finalize(() => recorder.push('finalize()')),
108-
)
109-
.subscribe((value) => recorder.push(value));
110-
});
111-
}
112-
}
113-
114-
const fixture = TestBed.createComponent(TestComponent);
115-
fixture.destroy();
116-
117-
// Wait until the `ngOnDestroy` microtask is run.
118-
await Promise.resolve();
119-
120-
// Ensure the `value` is not recorded, but unsubscribed immediately.
121-
expect(recorder).toEqual(['finalize()']);
122-
});
12379
});

packages/core/src/linker/destroy_ref.ts

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import type {EnvironmentInjector} from '../di';
9+
import {EnvironmentInjector} from '../di';
1010
import {isDestroyed} from '../render3/interfaces/type_checks';
11-
import type {LView} from '../render3/interfaces/view';
11+
import {LView} from '../render3/interfaces/view';
1212
import {getLView} from '../render3/state';
1313
import {removeLViewOnDestroy, storeLViewOnDestroy} from '../render3/util/view_utils';
1414

15+
const EXECUTE_CALLBACK_IF_ALREADY_DESTROYED = false;
16+
1517
/**
1618
* `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior.
1719
* The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
@@ -65,21 +67,9 @@ export class NodeInjectorDestroyRef extends DestroyRef {
6567
override onDestroy(callback: () => void): () => void {
6668
const lView = this._lView;
6769

68-
// Checking if `lView` is already destroyed before storing the `callback` enhances
69-
// safety and integrity for applications.
70-
// If `lView` is destroyed, we call the `callback` immediately to ensure that
71-
// any necessary cleanup is handled gracefully.
72-
// With this approach, we're providing better reliability in managing resources.
73-
// One of the use cases is `takeUntilDestroyed`, which aims to replace `takeUntil`
74-
// in existing applications. While `takeUntil` can be safely called once the view
75-
// is destroyed — resulting in no errors and finalizing the subscription depending
76-
// on whether a subject or replay subject is used, replacing it with
77-
// `takeUntilDestroyed` introduces a breaking change, as it throws an error if
78-
// the `lView` is destroyed (https://github.com/angular/angular/issues/54527).
79-
if (isDestroyed(lView)) {
70+
// TODO(atscott): Remove once g3 cleanup is complete
71+
if (EXECUTE_CALLBACK_IF_ALREADY_DESTROYED && isDestroyed(lView)) {
8072
callback();
81-
// We return a "noop" callback, which, when executed, does nothing because
82-
// we haven't stored anything on the `lView`, and thus there's nothing to remove.
8373
return () => {};
8474
}
8575

packages/core/test/acceptance/destroy_ref_spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,24 @@ describe('DestroyRef', () => {
239239
expect(onDestroyCalls).toBe(2);
240240
});
241241

242+
it('should throw when trying to register destroy callback on destroyed LView', () => {
243+
@Component({
244+
selector: 'test',
245+
template: ``,
246+
})
247+
class TestCmp {
248+
constructor(public destroyRef: DestroyRef) {}
249+
}
250+
251+
const fixture = TestBed.createComponent(TestCmp);
252+
const destroyRef = fixture.componentRef.instance.destroyRef;
253+
fixture.componentRef.destroy();
254+
255+
expect(() => {
256+
destroyRef.onDestroy(() => {});
257+
}).toThrowError('NG0911: View has already been destroyed.');
258+
});
259+
242260
it('should allow unregistration while destroying', () => {
243261
const destroyedLog: string[] = [];
244262

0 commit comments

Comments
 (0)