Replies: 3 comments
-
It happens because ComponentStore.effect instantly subscribes to the given observable. I need to find some way to postpone this action till the constructor is evaluated. |
Beta Was this translation helpful? Give feedback.
-
So I've created a decorator, where I'm overriding "effect" method (types are omitted for brewity): effect(generator) {
const origin$ = new Subject();
let subscribed = false;
/// 🚧 generator().subscribe moved from here...
return ((observableOrValue?): Subscription => {
///
if (!subscribed) {
/// 🚧 .... to here
generator(origin$ as OriginType)
.pipe(takeUntil(this.destroy$))
.subscribe();
subscribed = true;
}
///
const observable$ = isObservable(observableOrValue) ? observableOrValue : of(observableOrValue);
return observable$.pipe(takeUntil(this.destroy$)).subscribe((value) => {
origin$.next(value as ObservableType);
});
}) as unknown as ReturnType;
} In my classes, I can just add this decorator without modifying the code, and later, when this issue is resolved, I can just remove the decorating line. Decoratorimport { Observable, Subscription, Subject, takeUntil, isObservable, of } from 'rxjs';
import { Injectable } from '@angular/core';
export function WithInitEffect<T extends { new(...args: any[]): { readonly destroy$: Observable<void> } }>(constructor: T) {
@Injectable() // https://github.com/angular/angular/issues/38966#issuecomment-898462525
class Decorated extends constructor {
effect<ProvidedType = void,
OriginType extends | Observable<ProvidedType>
| unknown = Observable<ProvidedType>,
// Unwrapped actual type of the origin$ Observable, after default was applied
ObservableType = OriginType extends Observable<infer A> ? A : never,
// Return either an empty callback or a function requiring specific types as inputs
ReturnType = ProvidedType | ObservableType extends void
? () => void
: (
observableOrValue: ObservableType | Observable<ObservableType>
) => Subscription>(generator: (origin$: OriginType) => Observable<unknown>): ReturnType {
const origin$ = new Subject<ObservableType>();
let subscribed = false;
return ((
observableOrValue?: ObservableType | Observable<ObservableType>
): Subscription => {
if (!subscribed) {
generator(origin$ as OriginType)
// tied to the lifecycle 👇 of ComponentStore
.pipe(takeUntil(this.destroy$))
.subscribe();
subscribed = true;
}
const observable$ = isObservable(observableOrValue)
? observableOrValue
: of(observableOrValue);
return observable$.pipe(takeUntil(this.destroy$)).subscribe((value) => {
// any new 👇 value is pushed into a stream
origin$.next(value as ObservableType);
});
}) as unknown as ReturnType;
}
}
return Decorated;
} If you can see that this approach has some flaws - please let me know, it would be much appreciated. |
Beta Was this translation helpful? Give feedback.
-
I can happily report that new Example from my code: @Component({
selector: 'npp-datasources-list',
standalone: true,
imports: [
CommonModule,
// ...
],
templateUrl: './datasources-list.component.html',
styleUrls: ['./datasources-list.component.scss'],
providers: [DatasourcesListStore, CollectionService],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DatasourcesListComponent {
protected readonly store = inject(DatasourcesListStore); // 💡
protected readonly dsState$ = this.store.dataSourcesCollection.state$; // here you can use "store" field already
} |
Beta Was this translation helpful? Give feedback.
-
In ES2022 order of class initialization will be changed - fields will be initialized before the constructor. It might be a critical change - for example, with the ES2022 target it's impossible to use
combineLatestWith()
in ComponentStore effects if a store has some DI-injected source for that pipe.If some of you already found a workaround (besides moving all the effects fields declarations into the constructor or some method that we call in the constructor), then please share your knowledge.
Beta Was this translation helpful? Give feedback.
All reactions