Published on

Understanding Reselectors

Authors

As a Frontend developer, there are a lot of times when you will be required to understand and use Redux1 in your react applications .

At a first glance redux may look daunting... especially when dealing with concepts like immutability, and modifying the state using actions. But day 1 with a new concept in any language is always like this. By week 3 actions and immutability will be second nature to you. That is just the first step.

Once you are comfortable with redux the next step is building large scale applications with dozens or even hundreds of moving parts.

In this article I'm assuming you have the basic foundations of redux and react, and want to further improve your code using Reselect2. This will allow you to make your components neater, more testable, reusable, and easier for your teammates to understand and improve on.

Shall we start?

Good!

Introducing Redux

Redux is a simple framework consisting of a plain object that is used to manage the state in a front-end application and functions called actions and reducers used to update the object.

Think about all the React useState() operations that you might have in a component. Extract the different states from the components and centralize them in a parent component and you have a crude form of redux. It is arguable that React context can achieve the same, but we'll cover that in a different article.

Think of a music website like Soundcloud. Let's simplify it to the bare minimum. Using a layered3 approach we'll create the following components:

  1. Playback. What is playing and how far has it progressed. At what volume etc
  2. Track list display. What tracks do you have available to select for playback

At some point you will need to show the person:

  • In this tracklist track 1 is currently playing
  • This is how far it has progressed.

When the track completes playing you need to:

  • update the playback component display
  • then request the next track in the track list and
  • update on the track list that track 2 is now currently playing and
  • track 1 is no longer active.

If we have the data for the playback and the track list stored in the respective components it will be a bit hellish to come up with the logic for all this then have them interact through a complicated network of callbacks managed by a parent component.

Let's extract all the information from the playback and track list component and move them to a state higher up the application. In fact, let's have that state manage all data requirements our application may have in the future. Now we have a basic idea of what and why we need the Redux library.

Things like setup, dispatching actions and updating redux are not within the scope of this article. The documentation has covered it pretty well. Instead, we'll cover the nuances of reading from redux and how to make things easier.

Reading from redux

There are several ways to get information from redux to your component. The easiest would be through prop-drilling i.e a component higher up in the application reads the state then passes it through the props until it reaches the component on which it will be used. Prop drilling is ok when there's one or two levels of nesting. Beyond that it is just overcomplicating things and reducing readability.

The second and most common way of accessing the redux state is through the useStore hook.

const store = useStore();
const state = store.getState();

This gives you the whole redux state. It is ok to do this and access to a part of the state you need can be done this way state.tracks.tracklist. What if I told you there's an easier way to do it?

Yup...which brings us to the useSelector hook. This hook allows us to traverse the state object and reach the part of the state object we need. We would have something like

const tracklist = useSelector((state) => state.tracks.tracklist);

But wait, isn't that like useStore but using a different hook that removes one step? Yeah it is, and good eye!

But useSelector carries the advantage of being able to receive a utility function that receives state in the first argument and do with the data whatever it needs to. This function could also be a reselect, which we'll discuss in a bit.

A lot of times when using useStore or useSelector it felt like I was just doing the same thing over and over espceially when different components needed similar data.

Even worse when designing components using storybook4 I always had to provide the redux state to allow me to test them in contrast to the nifty utilities storybook provides. This made everything clunky and annoying.

React components ideally should be pure functions with consistent output for the provided input. Accessing state from within components muddies this aspect.

So how do we get around this? How do we access the state in an easy predictable manner without excessive prop-drilling and retain the pure function nature of our components. Aren't we trying to have our cake and eat it?

No, Marie. We can have the best of all worlds. We can reduce prop drilling AND access the node in our state that we need. As a bonus how about we throw in the ability to preprocess by merging, calculating and refining our state data without having to overcomplicate it by having extra nodes with our recalculated values, and without making the component do the processing.

So now you might be tempted to be like, "Dear God, Ben, that was wordy, are you a snake oil salesman now?"

I promise I'm not. I'm such a bad salesman, software, writing, research and animal farming were the only viable career options for me!

On to Reselect

Reselect is the most powerful yet underused tools in the redux-toolkit arsenal for large scale React-Redux projects.

In a nutshell imagine the Redux state is a cake; Reselect is the knife that allows you to slice it into a bunch of palatable sizes. Is the slice you have still too big? Reselect into smaller bits until you have something you can fit in your mouth.

Still on the cake metaphor, do you hate cream and want to remove it from your slice of cake? You don't have to remove the cream from the whole cake, others probably love and want the cream; reselect is still the knife you will use to scrape the cream off of the slice you have.5

For people coming from a backend background you can break it down this way:

  • Reselect is the consumer in a message-broker system
  • Redux is a crude approximation of a broker like Kafka or RabbitMQ
  • The state is a message queue
  • A React component using redux is an application
  • The React app is the distributed system.

With reselect your application is less prone to runtime bugs that are introduced through calculation or data modification logic within components.

Onto the examples.

Let's go back to the Soundcloud example we started off with. We have a redux state like this

Redux
{
tracks: {
trackList: [], // list of tracks we can play at this point
},
//... something else
playback: {
currentTrack: null, // track that is currently playing
playing: false, // is the music supposed to be actively playing or nah
muted: false, // can we hear it
volume: 100, // how loud is it?
}
}

Apart from the basics provided in the documentation we could do so much more.

The basic structure of a reselect looks like this

const selector = createSelector(...input, (...output) => { return doSomething(output) });

A reselect can receive an unlimited number of selects as the input, and the select output function which is the last argument of createSelector receives an equal number of arguments each containing the result from the selectors the received.

This means we can pass the data through several layers of reselects each one refining the data as needed.

In this article selectors shall be wrapped in functions prefixed by makeSelect rather than holding them in 'naked' variables. This means that the function has to be invoked to receive the selector.6

Let's say we have a component that is supposed to display the track list.

We'll create the following selectors7

trackSelectors.js
export const selectTrackDomain = state => state.tracks;
export const makeSelectTrackManager = () =>
createSelector(
selectTrackDomain,
substate => substate,
);
export const makeSelectTrackList = () =>
createSelector(
makeSelectTrackManager(),
state => state.trackList,
);

Then our TrackList component could look like this

component/trackList.js
import { useSelector } from 'react-redux'
import { makeSelectTrackList } from './trackSelectors'
import Track from './component/track'
export function TrackList() {
const tracks = useSelector(makeSelectTrackList())
return (
<div>
{tracks.map((track) => <Track key={track.id} {...track} />)}
</div>
)
}

Nice, huh?

Let us assume that we want the track items to show the track that is currently loaded in the player...the one that is active, so to speak.

We could pull the currentTrack property directly from redux and use the current track id with the track list to identify the current active track like this.

component/trackList.js
import { useSelector, useStore } from 'react-redux'
import { makeSelectTrackList } from './trackSelectors'
import Track from './component/track'
export function TrackList() {
const store = useStore();
const state = store.getState();
const currentTrack = state.playback.currentTrack;
// const currentTrack = useSelector((state) => state.playback.currentTrack) // Alternative to line 7-9 using `useSelector`
const tracks = useSelector(makeSelectTrackList())
return (
<div>
{tracks.map(
(track) => (
<Track
key={track.id} {...track}
isActive={currentTrack?.id===track.id}
/>)
)}
</div>
)
}

The Track component can then use the isActive prop to highlight the component using css styling.

Let's take it further and create a few more reselectors and refactor the TrackList component a bit.

playbackSelectors.js
export const selectPlaybackDomain = state => state.playback;
export const makeSelectPlaybackManager = () =>
createSelector(
selectPlaybackDomain,
substate => substate,
);
export const makeSelectCurrentTrack = () =>
createSelector(
makeSelectPlaybackManager(),
state => state.currentTrack,
);
trackSelectors.js
// Track selector utilities
import { makeSelectCurrentTrack } from './playbackSelectors';
export const selectTrackDomain = state => state.tracks;
export const makeSelectTrackManager = () =>
createSelector(
selectTrackDomain,
substate => substate,
);
export const makeSelectTrackList = () =>
createSelector(
makeSelectTrackManager(),
state => state.trackList,
);
export const makeSelectEnhancedTrackList = () =>
createSelector(
makeSelectTrackList(),
makeSelectCurrentTrack(),
(trackList, currentTrack) => trackList.map((track) =>
{
const isActive = currentTrack?.id === track.id;
return {...track, isActive};
}),
);
component/trackList.js
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { makeSelectEnhancedTrackList } from './trackSelectors'
import Track from './component/track'
export function TrackList({tracks}) {
return (
<div>
{tracks.map(
(track) => <Track key={track.id} {...track} />)}
</div>
)
}
TrackList.propTypes = {
// We'll not go into the track item shape particulars apart from isActive
trackList: PropTypes.arrayOf(PropTypes.shape({}))
}
const mapStateToProps = createStructuredSelector({
tracks: makeSelectEnhancedTrackList()
});
export default connect(mapStateToProps)(TrackList);

So, that looks like a lot to take in. I promise it's not. Let us focus on the highlighted lines.

We have 3 files to look at

  • playbackSelectors.js which is new and contains selectors accessing the playback slice of the state
  • trackSelectors.js which contains a new selector
  • trackList.js which has been completely refactored

Playback selectors

The playback selectors are a list of selectors concerned with obtaining information about the playback. In a real world application there may be details regarding the current time, total duration, the volume, is the player muted etc.

Our interest is in the makeSelectCurrentTrack selector which provides us with the current track that is currently playing. Very straight forward.

Track selectors

We have modified this selector group to include one more selector makeSelectEnhancedTrackList which is a composition of two other selectors, the track list and the currentTrack from the playback selectors group.

Using the information provided this selector outputs a list of modified track objects that include an isActive property which fulfills our need to identify which track is currently playing. All without any processing within the TrackList component.

The trackList component

This component has several noticeable changes, the key one being that we are receiving the track list through the props.

We are doing this because that we are using redux's connect to wrap TrackList before exporting it by default as a stateful component. This means that wherever it is used in our app the tracks prop will contain data provided by the makeSelectEnhancedTrackList reselect. The caveat here being that we have to use the default export i.e

import TrackList from './component/trackList'

createStructuredSelector is a redux utility that takes an object whose properties are selectors and returns and object where the values of the properties have been replaced by the results of the selectors. In our case we receive the track array. connect provides the redux state to the selector which reduces the lines of code you have to use significantly.

The other change is we are using makeSelectEnhancedTrackList instead of makeSelectTrackList. This means that the track items have an extra isActive property that can be used to identify which track item is currently active. When the currentTrack state is update the changes are propagated immediately. All without doing any processing in the component.

The bonus is that for testing in storybook or jest we have access to the stateless component through

import { TrackList } from './component/trackList'

Conclusion

So here we are.

We have created a neat stateful component that contains an isolated and modified chunk of the redux state.

Sometimes it may seem like a hassle to write out all the reselects needed for your use-cases, but ultimately it helps reduce bugs and clutter. When used alongside tests reselects reduce the amount of work required to track down bugs.

On large scale applications like ecommerce websites reselects provide reusability in cases where calculations like tax need to be done and displayed in several locations, and during the checkout process. In addition, ecommerce websites sometimes need to highlight the items in the item list that are already in the cart which is a straight-forward process if the product lists are passed through reselects.

The use-case for reselect is only limited by your imagination.

I hope this article has been useful to you, and all the best as you progress into an awesome engineer!

Please reach out to me on email for clarifications, re-edits and corrections.


  1. Redux core concepts
  2. Reselect Basic Usage
  3. This approach involves breaking down the application functionality and components depending on the logic getting handled. In this music website example we shall handle playback and track display separately
  4. If you're not using StorybookJs what are you waiting for? I'll probably do an article for this in the future
  5. Now I'm thinking of cake 🤤
  6. Why this? You will notice in the code examples that the function invocations are highlighted in a different colour to variables making them easier to pick out visually and tell which is the input part of the select and which is the output. The same visual differentiation also works on IDEs like Webstorm. Form follows function...literally :)
  7. I have not included tests, but reselectors are ideally pure functions, so creating them is a straight forward process.