DEV Community

Cover image for Understanding Derived state and Selectors.
Pushpendra Singh
Pushpendra Singh

Posted on • Updated on • Originally published at stud2design.in

Understanding Derived state and Selectors.

photo-1530404448622-ca16987ec6f7
Source Unslash by @wadeaustinellis

This article was originally posted in my blog.

Let's get started now.

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.

  • by cutting the cost where we can,
  • by using tested patterns and solutions.

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.

const mapStateToProps = (state) => {
  let userTitle;
  if (state.user) {
    if (state.user.gender === "Male") {
      userTitle = "Mr.";
    } else if (state.user.maritalStatus === "Married") {
      userTitle = "Mrs.";
    } else {
      userTitle = "Miss";
    }
  }
  const username= `${userTitle} ${state.user.firstName} ${state.user.lastName}`;

  return ({
   username
  });
}


Enter fullscreen mode Exit fullscreen mode

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.

  • Selectors can compute derived data, allowing Redux to store the minimal possible state.
  • Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
  • Selectors are composable. They can be used as input to other selectors.

Lets us refactor above used example to use a selector :


// index.js 
const mapStateToProps = (state) => {
  return {
    userName: selectUserName(state.user),
  };
};


// selector.js
export const selectUserName = (user) => {

  let userTitle;
  if (user) {
    if (user.gender === "Male") {
      userTitle = "Mr.";
    } else if (user.maritalStatus === "Married") {
      userTitle = "Mrs.";
    } else {
      userTitle = "Miss";
    }
  }

  return `${userTitle} ${user.firstName} ${user.lastName}`;
}

Enter fullscreen mode Exit fullscreen mode

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

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.


// index.js
const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state)
  }
}


// selectors.js
import { createSelector } from 'reselect'

const getVisibilityFilter = (state) => state.visibilityFilter
const getTodos = (state) => state.todos

export const getVisibleTodos = createSelector(
  [ getVisibilityFilter, getTodos ],
  (visibilityFilter, todos) => {
    switch (visibilityFilter) {
      case 'SHOW_ALL':
        return todos
      case 'SHOW_COMPLETED':
        return todos.filter(t => t.completed)
      case 'SHOW_ACTIVE':
        return todos.filter(t => !t.completed)
    }
  }
)

Enter fullscreen mode Exit fullscreen mode

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 you can run, test and play with it within a 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

  • A selector created with createSelector has a cache size of 1 and only returns the cached value when its set of arguments is the same as its previous set of arguments.
  • The default equalityCheck function checks for changes using reference equality, in default memoize function. Custom equality check example..
  • Best with the immutable redux store.

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.

Top comments (0)