ngrx selectors with filters
Solution 1
For that kind of scenario where I want to reuse part of a stream I create a function that accepts the store as a parameter and does some operations on it. The following is an example:
const selectMyState = (state) => state.myState;
export const getData = createSelector(selectMyState, state => state.data)
export const getDataWhenLoaded = (store) => {
return store.select(getData)
.filter(myState => myState.isLoaded);
};
...
getDataWhenLoaded(this.store).subscribe(...);
As far as using the last parameter of the createSelector
function, you can depending on your need. In the documentation it mentions that the same state passed to the memoized selector created by createSelector
will not recompute the projection. It will return the same value when invoked. I couldn't find documentation on this but through testing I noticed that if the projection results in the same value as the previous value then it will not be emitted if the subscription already received the previous value. Similar behavior to the rxjs operator distinctUntilChanged
. I haven't had time to dig through their source code to understand where/how/why.
So in some cases you can put a filter in the projection parameter (last) for createSelector
. Here is an example:
export const getActive = createSelector(selectMyData, state => state.data.filter(x => x.isActive));
Solution 2
The example is from ngrx.io
Assuming you have the selectValues selector and you want to filter the values making the code reusable.
import { select } from '@ngrx/store';
import { pipe } from 'rxjs';
import { filter } from 'rxjs/operators';
export const selectFilteredValues = pipe(
select(selectValues),
filter(val => val !== undefined)
);
store.pipe(selectFilteredValues).subscribe(/* .. */);
Related videos on Youtube
David
Engineering Manager with 15+ years of experience in the field. Avid learner. Servant leader. A true believer in the importance of feedback and communication as a means to achieve success in a team.
Updated on September 15, 2022Comments
-
David over 1 year
I'm using @ngrx in an Angular application and I'd like to abstract one of the recurrent patterns that I already have.
My state is typically is composed of many states like this:
myState: { data: any isLoading: boolean isLoaded: boolean }
so what I end up doing in the code is subscribing to it but I only want to be notified if the state has been loaded.
this.store .select(state => state.myState) .filter(myState => myState.isLoaded) .map(myState => myState.data) .do(data => do_whatever_I_need_to_do) .subscribe();
this works for me, but I was wondering if I could simplify the first 3 operators as they are quite recurrent.
By using selectors I could create something like this
const selectMyState = (state) => state.myState; export const getData = createSelector(selectMyState, state => state.data)
and then use it like this
this.store .select(getData) .do(data => do_whatever_I_need_to_do) .subscribe();
but then I'm missing the filter part, so I might get stream events from it even though my data has not yet been loaded.
Is there any way to include that missing filter into the observable? I know the createSelector has a function as param, so that I can do some code in there, but I'm not sure how I would use that to filter not data, but the event itself.
Any ideas? Thanks!
-
David over 6 yearsthanks @bygrace I'll have a look at adding this to the last parameter as you mention, see if it works
-
David over 6 yearsThat last bit didn't work, but the first solution about creating a function injecting the store does. It felt a bit hacky to me at first, but code feels clean enough and gives me what I wanted, so I will accept the answer.
-
bygrace over 6 years@David yeah, the last part wont work for your specific situation. I would probably only use it for lists at best but I really just use the first solution to avoid being tricky and relying on possibly undocumented features.
-
MrD about 5 yearsExactly what I need. Thx!
-
Skorunka František about 4 yearsOr
store.select(selectors.getData).pipe(filter((x) => !!x));
where!!x
is your condition. Don't forget toimport { filter } from 'rxjs/operators';