docs: Add DestroyRef.destroyed and usage in lifecycle guide#64422
docs: Add DestroyRef.destroyed and usage in lifecycle guide#64422SkyZeroZx wants to merge 1 commit intoangular:mainfrom
Conversation
| By checking `destroyRef.destroyed`, you can prevent executing code after the instance has been cleaned up, avoiding potential errors such as | ||
| [`ExpressionChangedAfterItHasBeenCheckedError`](errors/NG0100). |
There was a problem hiding this comment.
I'm not that's correct. IIRC it was to address issues where NG0911 occured (view already destroyed), see #54527.
There was a problem hiding this comment.
Updated, on the other hand we do not have an error guide for NG0911, would it be good to add one too?
| constructor() { | ||
| this.search.valueChanges.pipe(debounceTime(300)).subscribe(value => { | ||
| if (!this.destroyRef.destroyed) { | ||
| this.performSearch(value); | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
I'm not sure this example makes much sense. Users would use takeUntilDestroyed() instead.
I don't have an obivous example that comes to mind, maybe @atscott can chime in here.
There was a problem hiding this comment.
I was thinking a bit, what do you think of this example, the case of an animation or a background process like a heavy web worker could be a more suitable example?
@Component({ /* ... */ })
export class ProgressBar {
private destroyRef = inject(DestroyRef);
progress = 0;
ngAfterViewInit() {
const start = Math.floor(performance.now());
const step = (now: number) => {
// stop if component was destroyed
if (this.destroyRef.destroyed) return;
const elapsed = now - start;
this.progress = Math.min(100, (elapsed / 1000) * 100);
// schedule next frame while still alive
if (this.progress < 100) requestAnimationFrame(step);
};
requestAnimationFrame(step);
}Or
@Component({ /* ... */ })
export class WorkerProcess {
private readonly destroyRef = inject(DestroyRef);
progress = 0;
onProcess() {
// Web Worker that performs heavy computation off the main thread
const worker = new Worker(new URL('./heavy.worker', import.meta.url), { type: 'module' });
worker.onmessage = (e: MessageEvent<{ progress: number }>) => {
// stop if component was destroyed
if (this.destroyRef.destroyed) return;
this.progress = e.data.progress;
};
// Kick off heavy task
worker.postMessage({ cmd: 'start' /*, payload: bigData */ });
}
}There was a problem hiding this comment.
I don't know if there are really any good examples. When writing something from scratch or when the example is simple, there are often other ways of getting there without destroyRef.destroyed. The worker example is maybe fine-ish but arguably too complicated as an example still. To be honest, any example for this is probably beyond what the documentation should provide. There's no reason to have someone try to understand the purpose of the example code and try to figure out why/if DestroyRef.destroyed is necessary in that case.
There was a problem hiding this comment.
+1, let's remove the example.
30161a3 to
a15616e
Compare
a15616e to
bee3067
Compare
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information