paint-brush
Selectors in Redux are a MUSTby@riccardoodone
37,482 reads
37,482 reads

Selectors in Redux are a MUST

by Riccardo OdoneFebruary 14th, 2018
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

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.

People Mentioned

Mention Thumbnail
featured image - Selectors in Redux are a MUST
Riccardo Odone HackerNoon profile picture

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

where locations could be

Does it look bad enough? Well, imagine the pain of doing an immutable update to check the node with id 3. In plain JavaScript that would look like this

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 1/2/3).

With locationsByPath the update looks much nicer

Separating concerns

While working on the feature I’ve noticed something: locationsByPath knows too much. As a matter of fact, it has both domain knowledge (ie id, value and hierarchy) and UI data (ie checked).

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

Also, checkedLocationsPaths and locationsByPath are independent now. That means, they can follow different lifecycles. For example, checkedLocationsPaths can be present in the store before loading locationsByPath.

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

with 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 resources.cat1).

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.

Outro

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.

Want to read more cool JavaScript stuff? Check out how functional programming can make your life easier.

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.