In this post I want to share with all of you something I consider very important and that I’ve noticed in lots of code repositories regarding the use of state selectors and the useSelector hook. I assume you already have some knowledge or experience working with state management libraries specifically Redux, hope to make it very clear and without further ado, let’s begin.
Imagine you have a state like this:
And then in your components you do something like this to get your state and use it:
So… What’s wrong with this?
I’ve seen this in lots of places, and the issue is that it causes multiple unnecessary re-renders, why? The shallow equal compares every field inside the selected object (tasks in this example) to identify if it has changed, if it considers it did then it will re-render the component that has the selector.
This means if you only need the list from the tasks state and from any other place the sortBy or the apiFilters change, then any component accessing the list with the code above will be re-rendered unnecessarily.
How do we avoid this?
With this approach:
This way we access only what we need (the list itself) and the reference or shallow comparison is done properly avoiding unnecessary re-renders.
Oh! What if I need multiple fields from the state?
Note that if you want to avoid shallowEqual for some of the selected objects you could just separate the calls.
But then I suggest going further to avoid so much code (which could also be duplicated) for getting some state objects, and if you extract each selector logic as custom hooks you end up with a lot of specific state selectors, so how do we come up with a general generic and reusable solution?
This is a function that returns another function (the selector itself), hence we call it getSelectField.
It is a totally generic and reusable general solution for every component and side effect (if using redux sagas) in any app, and to make it simpler you can define the selector in every slice and export it from there to avoid having to do that also multiple times.
As a side note, if you don’t know what I mean by “slice”, you might not be aware of Redux Toolkit which is an amazing library that will allow your team to work with Redux in a simpler way with easy setup and scalability.
So in your reducer’s slice:
Here you just go ahead and create a generic and reusable selector hook to use in components:
Note that for the sake of demonstrating we set shallowEqual to be used by default as the equalityFn, you can avoid that if you consider it not necessary for your selector.
Now in your component you just do:
Or for selecting multiple state objects at once:
What about selecting nested objects?
The good thing about this is that since we are using get from lodash you can actually access elements at any level in the state by passing parent.child.anotherChild as fieldName.
So if we had a state such as:
Assuming we had a useSelectTasksDetails selector hook, we could select the parentTaskGroup by doing:
Or for selecting multiple state objects at once.
IMPORTANT: For this to work as expected, ensure you always select ONLY what you need. Don’t select state objects that contain fields that you won’t use, if you do so then everytime any of those get updated it will trigger a rerender even though you’re not actually using that field/object.
I hope it was clear enough, and you see the benefits of staying DRY.
Get in touch!
403 Portway Ave #300,
Hood River, OR 97031 firstname.lastname@example.org 541-288-4033