Home > Mobile >  NGRX filter opererator in selector throws source.lift is not a function exception
NGRX filter opererator in selector throws source.lift is not a function exception

Time:08-26

Using NGRX and Angular 13 (just for clarity), I have a store with the following state:

export const initialState: SomethingState = {
  data: null,
  succeeded: false,
  message: "",
  errors: null,
};

And it has a feature selector as such:

export const selectSometingFeatureSelector = createFeatureSelector<SomethingState>("somthing");

Each time an API call completes the 'succeeded' value on the state is updated to true (provided no errors happened).

Then in each component where I use the 'selectSometingFeatureSelector' I always check to make sure the call has succeeded (and sometimes use a take(1) afterwards to auto unsubscribe). For example:

this.store.select(selectSometingFeatureSelector)
.pipe(
      filter((state) => state.succeeded),
      take(1),
      map((state) => {
        return state.data.items;
      })
);

This is all fine (I think) but I find myself repeating the code a lot as all our states have 'succeeded' on them.

I wanted to move this to a selector so at first I wrote the below but it didn't work as the observable still fired before succeeded:

export const selectSomething = () =>
  createSelector(
    selectSomethingFeatureSelector,
    (state: SometingState) => {
      if(state.suceeded) {
      return return state.data.items;
    }
  }
);

Looking at the code I'm not that surprised it didn't work (but it was worth a shot). Then after more research it seemed like this was the proper approach:

export const selectSomething = () =>
  createSelector(
    selectSomethingFeatureSelector, 
    filter((state: SomthingState) => state.suceeded === true)
    (state: SometingState) => {
      return return state.data.items;    
  }
);

Whilst the above seems to make perfect sense to me and I get no complaints in VS code I get the following error in the console:

TypeError: source.lift is not a function at filterOperatorFunction

So my question is in 2 parts:

  1. Am I right to be moving this to a selector or should I keep it in the component?
  2. If a selector is the right way to go then what have I done wrong?

CodePudding user response:

For the first question I think is a matter of preference, both are valid ways of doing things i personally think its cleaner to have it done in the selector and just import that selector to the component. But there isn't any right or wrong here.

For the second question, it looks like you are giving the createSelector a second argument, filter() function which is wrong. You want something along these lines as you did from the beginning (as per the documentation) :

export const selectSomethingSucceeded = createSelector(someThingState, (state: SomethingState) => {
 if(state.succeeded) {
     return state.data.items
   } else {
     // Assuming data is an array
     return [];
   }
});

Its similar to the first attempt you did but without the double returns and a else block to handle null.

Also you should be vary with assuming a unsubscribe is happening when you use take(1) as they arent the same thing, more here

CodePudding user response:

The rxjs filter operator is not meant to be used in `createSelector() but you can do a pipeable operator :

export const selectSomething = pipe(
  select(selectSomethingFeatureSelector),
  filter(val => state.suceeded === true)
  map((state: SometingState) => state.data.items)
);

Which can be called like this :

store.pipe(selectSomething).subscribe(/* .. */);
  • Related