What you should know about @ngrx selectors
So i am writing this article to be able before all to save time answering the same thing in the Gitter channel of @ngrx and reference the article in futur to let new users of the lib know more quickly this little tip about selectors.
First you can check the official documentation about selectors here.
So to make the story short the selectors in your basic use of the library allow you to access a piece of state from store in your smart components.
What is behind ngrx selectors?
Basically the ngrx selector is:
pipe(map(yourSelectorMethod), distincUntilChanged())
distincUntilChanged
operator from rxjs uses a ===
.
So if you select in your component a value of type string, number or boolean
, it’ll not trigger the selector in the component when the value doesn’t change.
But if you select an array or object
, it’ll trigger always at store update because the rxjs map
will create a new reference then the distincUntilChanged
will see a new reference in the selector.
Example about one problematic use case with ngrx selectors
When you use ngrx/entity/selectors => Official documentation here.
In the documetation from the link on top, let say i want to update the dom when the current user changes. You have multiple options:
Option0: [NEW ng-conf may 2019 Add On]
Custom selector option
please watch this video, i pinned the right moment: https://youtu.be/RXuSDiLmcN0?t=622
Option1: (the way you should probably not do it)
You can use directly in the component the selector selectCurrentUser
.
This selector select a none primitive type.
What did we say in the section before!! object
are not protected in the selector. Which means for example if you update the current list of users (entities) in the store then the selector selectCurrentUser
will trigger even if the current user never changes => it’s the same.
Option2: (the way you should probably do it)
You can use instead selectCurrentUserId
. With this selector you are selecting a state of type string or number
. Perfectoo!! no more issue in the component if we alter the current list of users (entities) in the store.
But you ll ask! i need the whole object user not only his id in the component. How to do this?
Your component select with this strategy:
this.store.pipe(
select(selectCurrentUserId),
withLatestFrom(selectCurrentUser)
).subscribe(([currentUserId, currentUser]) => {
// you can avoid this subscribe by using async pipe
…
});
This will only update if the current user ID really changes.
note: withLatestFrom
approach shown on top may not be the best way to select. Please follow discussion here to find out about another way to select.
Option3:
Wait a sec! some of you will say:
Ok! but what about if i want to watch when the current user changes but also watch for changes inside the current User attributes, If something else than the id changes like his name or email. The Option2 will not work anymore to update the component with the new state.
In this case, what you can do! hummmmm!
You can use the selector selectCurrentUser
and add anotherdistinctUntilChanged(param1, param2)
operator (example here)but this time passing some parameters to it to be able to compare the previous value vs the new with a deep equality check.
OR
You can create a selector selectCurrentUserHash
that hash the current User => you’ll endup selecting a string
then your selector is protected from list update as in Option2. And as in Option2 you can use the withLatestFrom
in the component to access the current user object
.
OR
You can create selectors for the attributes that changes inside the current user and use them directly.
Conclusion
Voila voila! this example is a really simple use case.
you can extend the content of this article for different use cases in your app (including none ngrx/entity selectors).
You are the artist :) so think twice about the selectors you are using in your components to avoid especially having junior devs after you messing up the App.
Keep up with our content by following us @FrontEndTricks & don’t forget to give it some 👏👏👏.
~ Thank you ~