Why is Redux still important? Part 2

Oscar
eDreams ODIGEO
Published in
6 min readApr 22, 2021

In our first post we saw how to connect our models from our own framework to Redux but each model was receiving the whole store on each update. This is not desirable as a model should only know the minimum necessary information from the store as it is easier to control what we have in it and react with precision to given changes. In this post we are going to see how we can achieve this continuing with the code we built in the previous post.

Less information is better

This was our implementation from the previous post

Here, we are sending store.getState() (the whole Redux store) to the model class. But we do not want it to have the whole store for various reasons:

  1. A model should contain just the pieces of information it needs. Consequent changes to the store will fire updates on it. If all models contain the whole store, on any update we will need to update all the models. This is not desirable at all for performance reasons
  2. Future problems are easier to debug. If our model is not working as expected and we are just sending a concrete part of the store we can determine where our problem is in our application.

The solution? What if the model can specify which pieces of the store are relevant for him and on next updates we just send those parts only if they have changed? Let’s try it.

Selectors

What we want to achieve is to be able to specify at the initialization of each model the parts of the store me, as a model, want to be notified when they change. Let’s see an image to clarify this.

The component1 only needs the property in the store called innerPropertyOfPart1 and the component2 only needs the one called innerPropertyOfPart2. So why sending everything to both components?

Let’s start by adding these selectors at the initialization of each model

Component 1
Component 2

We have just added an array of selectors to the properties of the model. Easy.

Now, we need to create the access to these parts of the store. We will have a function called as the selector name, it will receive the whole store and return just the part we want.

Let’s keep it that simple for now.

Once each model knows exactly what is the information he needs and we have created the access to it, we just need to connect the model to these functions. In order to do so, we will modify the code in our store file.

Let’s understand this function step by step.

if (Array.isArray(modelClass.selectors)) {
// ...
}

First of all we check if the model has the property selectors, as some of our components will not need to be connected to it.

modelClass.selectors.forEach((selector) => {
// ...
});

Then we iterate all the selectors defined in each model, having the name of it inside the callback function in the variable selector

const selectorValue = selectors[selector](store.getState());

With the name of the selector, and all the selectors in an object, we can access directly. And as they were functions expecting the store as the parameter, we can execute them as follows.

if (selectorValue) {
modelClass.set(selector, selectorValue);
}

And finally, if any value was returned from the selector, we can call a function inside of our model. In this case we are calling the set function, which will include in the model attributes map the selector and the selectorValue, but it can be any function in your model.

Optionally, at the end of the execution of the loop we can call

triggerModelChange(modelClass);
From previous post

In case we want to execute a function in each model to let him know the updates on it have finished.

Now, each model knows when it has been updated. Let’s see how simple it is to add a new attribute to a model from our store.

Let’s say now the component1 needs to have the property we called yetAnotherProperty inside the part2 of our initial store. We will just need to add a new entry in the selectors array:

And create the corresponding path in the selector

And that’s it.

Now, the component1 will be notified of the changes of that particular property, not caring about if any other property inside the part2 is modified. Isn’t it simple and beautiful?

Reselect

In the last example we can see that we are duplicating the access to the innerPropertyOfPart2 for our two selectors. This is not ideal thinking on bigger applications where we might have dozens of selectors. But there is a particular library that will help us to avoid that duplicity and also improve our performance.

Reselect (https://github.com/reduxjs/reselect) aims to create composable selectors, not having to duplicate accesses to our store. Even more, it caches the result of each selector, meaning that if the input for that selector is the same as before, it will not recompute anything, just returning the previous value. If we are using Redux and selectors in our application, having Reselect is almost a must.

How does our application look like with Reselect?

Breaking this into pieces

We import the createSelector function from Reselect and create a function that will receive the whole store and just return the part of the reducer this selector should work on.

import {createSelector} from 'reselect';const getPart2Store = (store) => store.reducer2;

The createSelector receives the function we have just created and returns the desired property.

const innerPropertyOfPart2Selector = createSelector(getPart2Store, (part2) => part2 && part2.innerPropertyOfPart2);

To call it we just need

export default {
innerPropertyOfPart2: (store) =>
innerPropertyOfPart2Selector(store),
};

With this, we are now using Reselect in our application, taking advantage of the caching and simplicity of it.

But the interesting part comes now. As we want to retrieve the property yetAnotherProperty inside the part2, we can compose that selector as follows

const yetAnotherPropertySelector = createSelector(innerPropertyOfPart2Selector, (innerPropertyOfPart2) => {
return innerPropertyOfPart2 &&
innerPropertyOfPart2.yetAnotherProperty;
});

We pass the previous selector which will return the whole property. Then, we access the one we want without needing to replicate the code as before.

Simple, elegant and performant. Redux + Reselect.

In this post we have seen how to connect our models to a given piece of the store instead of having to manage the full state of the application inside each component. We have seen how to connect the models to the selectors and the advantages of using the Reselect library.

In the GitHub repository you have an example of the implementation we have just talked about. Feel free to open a pull request to suggest or ask any doubt you may have. https://github.com/OBellon/vanilla-redux

And it does not end here! In our last post we will talk about how Redux allowed us to migrate to newer technologies in eDreams ODIGEO. Stay tuned!

--

--

Oscar
eDreams ODIGEO

Do. Or do not. But there is no try. Having fun as a Frontend developer @ eDreamsOdigeo