Stud2design LogoBlog

Derived state and using Selectors in React and Redux

publish date

22 May, 2020

read time

5 mins

coverSource: https://unsplash.com/@wadeaustinellis

When working with React our approach is always towards minimizing the no. of re-renders to gain performance. As we all are aware of the fact that in React render depends on two things state and props, so to reduce the no. of re-renders we have to start by controlling the state and props updates, by avoiding unnecessary or redundant updates.


React has always been careful with this practice and as a solution, it provides us with two APIs for class and functional component PureComponent and React.memo.


React PureComponent makes sure that a component is not updated until and unless there is an update in its props and state.


React.memo is a higher order function, React.memo only checks for prop changes.


Note : Both these APIs use shallow comparison.


So there is that. 🥱


Using State Management Library

Often when developing a React application we end up integrating redux or any other state management library to have a shared state across the App.


Integration of Redux is not cheap, it comes with a cost and it is highly recommended that if possible we should avoid using Redux in a small application and rather use React context API for managing the state, after all Redux is built over React.context API.


So, now that we are using Redux what we can do to use it smartly 🧐

Therefore what we have discussed above about React components with redux being part of our state management library same responsibility falls on it. Redux should also avoid contributing any updates to props that are redundant or lead to an unrequired recalculation of state. If you don't have experience with redux, I encourage you to go through their getting started guide.


How It can be Achieved

Firstly, you can start by using an immutable library like immutable.js or immer for your redux states.


Moving on 🏃‍♂️.


In redux rather than passing everything from store to component and calculating that data in our components and state, we can first derive the required state at the redux layer in mapStateToProps.


For example, calculating user name from multiple user fields. Lets abstract that logic from component to mapStateToProps.


1const mapStateToProps = (state) => {
2 let userTitle;
3 if (state.user) {
4 if (state.user.gender === 'Male') {
5 userTitle = 'Mr.';
6 } else if (state.user.maritalStatus === 'Married') {
7 userTitle = 'Mrs.';
8 } else {
9 userTitle = 'Miss';
10 }
11 }
12 const username = `${userTitle} ${state.user.firstName} ${state.user.lastName}`;
13
14 return {
15 username,
16 };
17};

But using the suggested flow of data manipulation introduces code smell, also the separation of concern is an issue now as mapStateToProps is doing more than just mapping the store state to props.


Introduction to Selectors.

We can use selectors for the derivation of data. Using selectors adds the benefits of reusing the derivation state logic across the app. Selectors are JS functions used to refactor our code, nothing special here, it is more of a pattern which makes selector so popular.

Lets us refactor above used example to use a selector :

1// index.js
2const mapStateToProps = (state) => {
3 return {
4 userName: selectUserName(state.user),
5 };
6};
7
8// selector.js
9export const selectUserName = (user) => {
10 let userTitle;
11 if (user) {
12 if (user.gender === 'Male') {
13 userTitle = 'Mr.';
14 } else if (user.maritalStatus === 'Married') {
15 userTitle = 'Mrs.';
16 } else {
17 userTitle = 'Miss';
18 }
19 }
20
21 return `${userTitle} ${user.firstName} ${user.lastName}`;
22};

By introducing selectors we have abstracted out the logic for username, now anywhere in our application where we need username we can use the selectUserName.


There is still an issue with above code. 🤷‍♂️


If there is an update in the redux state tree due to any other reducer, the selector will re-calculate the value which will result in re-renders. If the state tree is large, or the calculation is expensive, repeating the calculation on every update may cause performance problems.


To solve the above problem, we can memoize our selector, in that case, the selector will re-calculate new value only if its arguments change.


Using Reselect

For using the memoized selector and other patterns around selector we will now use the reselect library by redux. To explore reselect APIs we will be using todos example. I know right, another todos example nothing innovative here. Sorry.


Let's define a memoized selector named getVisibleTodos using createSelector from reselect.

1// index.js
2const mapStateToProps = (state) => {
3 return {
4 todos: getVisibleTodos(state),
5 };
6};
7
8// selectors.js
9import { createSelector } from 'reselect';
10
11const getVisibilityFilter = (state) => state.visibilityFilter;
12const getTodos = (state) => state.todos;
13
14export const getVisibleTodos = createSelector(
15 [getVisibilityFilter, getTodos],
16 (visibilityFilter, todos) => {
17 switch (visibilityFilter) {
18 case 'SHOW_ALL':
19 return todos;
20 case 'SHOW_COMPLETED':
21 return todos.filter((t) => t.completed);
22 case 'SHOW_ACTIVE':
23 return todos.filter((t) => !t.completed);
24 }
25 }
26);

In the example above, getVisibilityFilter and getTodos are input-selectors. They are created as ordinary non-memoized selector functions because they do not transform the data they select. getVisibleTodos, on the other hand, is a memoized selector. It takes getVisibilityFilter and getTodos as input-selectors and a transform function that calculates the filtered todos list.


I have implemented the above example, so run can test and play within codesandbox 👩‍💻.


Edit Reselect Example


To understand the benefits of selectors, open the console in codesandbox and toggle the theme for a couple of times, what you will notice after reading the console is that, calculation of todos and rendering don't occur in case you use selector function in mapStateToProps.


Cool, so we are almost done now. ⏳


Selectors, as previously mentioned, are composable, a memoized selector can itself be an input-selector to another memoized selector.


To explore all the APIs provided by reselect please visit the docs, they have detailed examples and FAQ section.


Caveats

Conclusion

So, now we know when and how we can use a selector. Remember selector is nothing but a JS function, you can use it not only for the redux state but anywhere in your code where you see it fits. Also, I hope now you have a better understanding of selectors and you can make a decision on whether you require reselect library in your project or not.