'NgXs @selector doesn't support async functions

Inside my NGXS store I have the following async selector

@Selector()
static async mainState(store: IMyStore): Promise<IMyState> {
    return this.getActiveState(store);
}

If I now subscribe to this selector

@Select(MyStore.mainState) state$: Observable<IMyState>;

If I subscribe to this stream I receive promises not IMyState objects. So I have to do:

this.state$.subscribe(p => {
    p.then(state => { ... });
});

I can cast the promise into a rxjs stream but then I receive streams

this.state$.subscribe(s => {
    s.subscribe(state => { ... });
});

So my question is, is there a way to support async selectors in ngxs?



Solution 1:[1]

The @Selector is used to "slice a specific portion of the state from the global state container" (not to call async function within it as I know).

You can achieve what you're trying to do, like the following:

  • Create a new @Action to handle the async process.
  • Add a new property to your state to hold the mainState value.
  • After getting the result from async function, update the mainState with the proper value.
  • Create a new @Selector function to return the mainState.
  • Dispatch the created action within your component.
  • Add @Select property within your component to get the mainState from your state.

Your code could look like the following:

mystore.state.ts

@Selector()
static mainState(state: IMyStore): IMyState {
    return state.mainState;
}

@Action(LoadMainState)
async loadMainState(ctx: StateContext<IMyStore>) {
  const _activeState = await this.getActiveState(store);
  ctx.patchState({ mainState: _activeState });
}

example.component.ts

@Select(MyStore.mainState) state$: Observable<IMyState>;

ngOnInit(): void {
  this.store.dispatch(new LoadMainState());
}

Solution 2:[2]

Or if you still don't want to create an awkward piece of store you can flatten with switchAll(). Doing so you avoid ugly subscribe inside subscribe.

@Select(MyStore.mainState) stateHigherOrder$: Observable<Observable<IMyState>>;
state$!:Observable<IMyState>;    

ngOnInit(): void {
 this.state$ = this.stateHigherOrder$.pipe(switchAll());
}

And now this.state$ is what you wanted

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Amer
Solution 2 Gauthier Peel