Selectors in Redux are a MUST
Lately, I’ve been shocked by how many problems Redux’s selectors can solve. That’s why I’ve become a big fan and I’m sad to see that pattern treated as an advanced one. They are easy to understand and, at the same time, enable low coupling / high cohesive code at the Redux’s state level.
I got to know selectors a couple of months ago. In fact, I started working on a crazy search page for a project: besides a normal text query, users can select a number of filters. Thing is, filters come in all sorts and shapes: some are a set of checkboxes, some are nested trees of checkboxes, some trees can be filtered with a text box. Not only that, they produce really complex queries with combinations of AND and OR. Not only that, they interact between each other. And the list of “not only that”s goes much longer.
At the beginning I felt overwhelmed by all of that complexity. Eventually, I worked my way towards the light step by step. In this journey, selectors have saved my ass and become my best friend.
What follows is a story from the project on how I got to know selectors and why they are so awesome.
Keeping state flat
At the beginning I had a store shape similar to this
locations could be
Does it look bad enough? Well, imagine the pain of doing an immutable update to check the node with id
Of course, I could use some library like dot-prop but that would be like sweeping the code smell under the rug.
A better representation could be
Or the one I ended up with
with the trees’ hierarchies encoded in the paths (eg
1/2 is child of
1 and parent of
locationsByPath the update looks much nicer
While working on the feature I’ve noticed something:
locationsByPath knows too much. As a matter of fact, it has both domain knowledge (ie
value and hierarchy) and UI data (ie
That’s wrong because most of the times domain and UI data have different needs and lifecycles. For example, in my case, once the filters are loaded in the store they are never updated. On the contrary, the
checked property is toggled every time the checkbox’s state changes. Therefore, I decided to separate the two:
First of all this makes checking a node incredibly easy
locationsByPath are independent now. That means, they can follow different lifecycles. For example,
checkedLocationsPaths can be present in the store before loading
Unfortunately, refactoring to a flat state made my life easier with reducers but harder with components. In fact, the initial nested shape is much easier for components to deal with:
Well, I could add some logic to the component to handle the flat state. But is it the right place for that? I don’t think so. In fact, doing so would mean coupling the component with the state’s shape. Also, I believe in really dumb™ components.
Selector to the rescue
Reducers are in charge of writing the state to a specific shape. Therefore, it’s a good idea to have something as close as possible to them to handle the reading part. That’s where selectors come into play.
If you don’t want to take my word, listen to Dan Abramov’s.
As a matter of fact, having the
initialState, write side and read side in the same place is right on spot:
Let’s see how some the selector works with an example
nestedLocationsWithChecks(state) ready to be passed to the
Component from the previous section.
In other words, on one side the application works with flat state. Which is more convenient for data management. On the other side, the application works with nested data. Which is more convenient for the UI.
More importantly, they can evolve independently since selectors work as an anti-corruption layer and prevent leaking structural coupling.
Bubbling up a selector
In the previous section I’ve worked top to bottom by refactoring the state first and worked my way down to the component.
Let’s see how the inverse looks like by refactoring some code I’ve written before getting to know selectors.
There’s a page in the application where some articles are shown grouped by their category (ie articles belong to one category only). All the categories must be rendered always besides the last one which should be hidden if empty.
categoriesFrom performs the group by and translates the title for the current locale.
Component renders each category separately.
Component is too smart and is coupled to the state shape (eg
Let’s bubble that logic up to a selector:
Ever heard about dumb components? With selectors you get dumber components™: they just destructure
props and render them without any additional logic.
In general, the shape of the state is something only reducers should know about. The moment it leaks out of the store, code becomes structurally coupled.
Since reducers decide what’s the shape because they write it. It’s just common sense to make the “read” happen close to them with selectors.
As a rule of thumb, doing
state. is a mistake. The only place where that should be allowed is inside a selector. But only as long as the selector is colocated in the reducer which is responsible for that part of the state.
If you liked the post and want to help spread the word, please consider tweeting, clapping or sharing this. But only if you really liked it. Otherwise, please feel free to comment or tweet me with any suggestions or feedback.