Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fbdbf5b

Browse files
committed
fix(template): untrack subscription and unsubscription in push pipe
1 parent 944a489 commit fbdbf5b

File tree

1 file changed

+32
-19
lines changed

1 file changed

+32
-19
lines changed

libs/template/push/src/lib/push.pipe.ts

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
OnDestroy,
55
Pipe,
66
PipeTransform,
7+
untracked,
78
} from '@angular/core';
89
import {
910
RxStrategyNames,
@@ -165,7 +166,7 @@ export class RxPush implements PipeTransform, OnDestroy {
165166

166167
/** @internal */
167168
ngOnDestroy(): void {
168-
this.subscription?.unsubscribe();
169+
untracked(() => this.subscription?.unsubscribe());
169170
}
170171

171172
/** @internal */
@@ -179,26 +180,38 @@ export class RxPush implements PipeTransform, OnDestroy {
179180
private handleChangeDetection(): Unsubscribable {
180181
const scope = (this.cdRef as any).context;
181182
const sub = new Subscription();
182-
const setRenderedValue = this.templateValues$.subscribe(({ value }) => {
183-
this.renderedValue = value;
184-
});
185-
const render = this.hasInitialValue(this.templateValues$)
186-
.pipe(
187-
switchMap((isSync) =>
188-
this.templateValues$.pipe(
189-
// skip ticking change detection
190-
// in case we have an initial value, we don't need to perform cd
191-
// the variable will be evaluated anyway because of the lifecycle
192-
skip(isSync ? 1 : 0),
193-
// onlyValues(),
194-
this.render(scope),
195-
tap((v) => {
196-
this._renderCallback?.next(v);
197-
})
183+
184+
// Subscription can be side-effectful, and we don't want any signal reads which happen in the
185+
// side effect of the subscription to be tracked by a component's template when that
186+
// subscription is triggered via the async pipe. So we wrap the subscription in `untracked` to
187+
// decouple from the current reactive context.
188+
//
189+
// `untracked` also prevents signal _writes_ which happen in the subscription side effect from
190+
// being treated as signal writes during the template evaluation (which throws errors).
191+
const setRenderedValue = untracked(() =>
192+
this.templateValues$.subscribe(({ value }) => {
193+
this.renderedValue = value;
194+
})
195+
);
196+
const render = untracked(() =>
197+
this.hasInitialValue(this.templateValues$)
198+
.pipe(
199+
switchMap((isSync) =>
200+
this.templateValues$.pipe(
201+
// skip ticking change detection
202+
// in case we have an initial value, we don't need to perform cd
203+
// the variable will be evaluated anyway because of the lifecycle
204+
skip(isSync ? 1 : 0),
205+
// onlyValues(),
206+
this.render(scope),
207+
tap((v) => {
208+
this._renderCallback?.next(v);
209+
})
210+
)
198211
)
199212
)
200-
)
201-
.subscribe();
213+
.subscribe()
214+
);
202215
sub.add(setRenderedValue);
203216
sub.add(render);
204217
return sub;

0 commit comments

Comments
 (0)