Understanding Javascript Selectors With and Without Reselect

pearlmcphee
6 min readJun 12, 2018

--

While Selectors aren’t particular to Javascript, React, Redux or Reselect, this article will focus on their use in the context of those technologies and assumes you have a solid understanding of both React and Redux. So, let’s begin!

What are Selectors?

At their most simple, Selectors are simply functions that are used to select a subset data from a larger data collection.

A metaphor:

“An “open” sign hanging on a store door” by Mike Petrucci on Unsplash

Imagine being in a large clothing store looking for belt. You ask a store clerk for help. She know’s where belts can be found so she goes and retrieves them for you. In this example, the store clerk acts as a selector.

Selectors:
1) Have knowledge of the whereabouts or path to find a particular subset of data and
2) Return with the requested subset of data

In code:
A selector to retrieve belts from a store could look like this:

const getBelts = (state) => state.items.belts

Why Use Selectors?

Basic selectors (i.e simple functions that return a subset of data) are beneficial because they encapsulate knowledge of where to find that particular subset of data, they are also reusable and flexible. To see what I mean, let’s take a look at an example DisplayBelts.js component that isn’t using a selector to find belts.

What would happen if the business owners opened a cafe next door and the store of their online app was re-structured so that belts were now found here: state.shop.items.belts? The query in DisplayBelts.js's mapStateToProps function would need to be updated to become belts: state.shop.items.belts . Which isn’t too bad. But if the app had other components pulling out belts from the store then the developer would have to update the query in every place that’s pulling that information (not to mention updating all other queries that would be affected by the store restructure like DisplayHats , DisplayShirts, etc.).

What would it look like to create and use a selector that retrieves belts?

In the above example you can see that the basic selector, getBetls is just a function of state that returns a specified piece of state (in this case belts). If in the future the business owners decided to re-structure their store again, after updating relevant reducers the developer would only have to update getBelts.

Because selectors are just functions they are also composable:

To summarize, even the most basic selectors provide the encapsulation of knowledge of where to find data which leads the ability to write reusable and composable code.

Why Use Reselect to Create Selectors?

The short answer is: for performance as Reselect provides a wrapper for creating selectors that are memoized.

The examples of selectors that you’ve seen so far have only been responsible for retrieving data as it appears in the redux store. This would only likely be enough in small apps where the data that app needs can found directly in the store. However, in larger and more complex apps, it’s advisable to keep the store with minimal amount of information as it cuts down on repetition and generally helps to avoid highly nested data that results in more complex reducer logic. This is especially relevant for apps that are structured with a normalized store.

In any case— selectors can be used to compute derived data.

Going back to our business app, suppose we wanted to find the current value of all items in the store.

We could create the following selectors:

When you factor in the fact that generally speaking React components re-render whenever their or their parent’s state or props change — the computation of derived data can easily become expensive. The biggest benefit of Reselect is that selectors created with the library are memoized and therefore will only re-run if their arguments change.

Reselect’s Syntax

Reselect offers up a function called createSelector() that’s used to create memoized selectors. Here is example code from the docs that our business app examples built off of.

As you can see createSelector is a function that takes two arguments:

  1. Selector(s) — if you have multiple selectors they can be inputted as a comma separated (like shown above) or as an array.
  2. A tranformer function that takes the values of the selectors from the first arguments and uses them to select or derive relevant data

Using Reselect

To retrieve state in a component

This is the most basic use case and is implemented the same way that you would use a selector that wasn’t created with Reselect.

Replacing a mapStateToProps function with a selector
As a further performance enhancer, a React component’s mapStateToProps function can be replaced with a selector to cut down on the number of times the component is re-rendered.

Using CreateStructuredSelector

To cut down on boilerplate, Reselect offers a createStructuredSelector that can be used in place of acreatSelector function that is being passed to connect. It is most helpful to use in components that are pulling in a number of selectors.

Passing an argument to a selector

Passing an argument that’s a prop:
If you’re trying to pass a prop into a selector you’re in luck! In addition to state selectors can also accept props. Let’s look at an example:

“ But there is a problem!” — Reselect’s docs quoting Redux’s docs

If the component that’s using the selector with props is in multiple places you could end up with a memoization error. Why? Because if we have more than one instance of a component the selector is shared between all of these instances.

So on one call the props passed to getInStockShopItems would be props.category === ‘belts', in another instance it’s receiving props.category === 'dresses' and in another it’s props.category === ‘pants'. This is a problem because Reselect selectors have a cache size of 1, which means that in this case the selector’s cache will be shared between each instance and will recompute an unnecessary amount of times because its inputs would be changing for each ShopItems rendered.

The way around this is to ensure that each instance of a component has its own private copy of the selector and therefore is unaffected by the going ons (or props) of any other instance.

To pass props to a selector that’s private to that instance of a component you can take advantage of this fact:

“ If the mapStateToProps argument supplied to connect returns a function instead of an object, it will be used to create an individual mapStateToProps function for each instance of the container.” (x)

To accomplish this in practice:

  1. Create a function that returns a selector.

2. In the component that is using selector, create a function that returns a mapStateToProps object that uses the makeGetInStockShopItems function (created in step 1) to create a new copy of the selector.

This will create a private mapStateToProps function for each instance of the component.

3. Pass the makeMapStateToProps function (created in step 2), to Redux’s connect function where you would normally insert mapStateToProps

And that’s it!

Passing an argument that’s a static value:

In this case you can use a factory function. The example below is from the Reselect docs.

Passing an argument that is a dynamic value:

If you’re passing dynamic values to your selectors, Reselect suggest that value should be in your store and available via state and thus you wouldn’t need to pass in the value, the selector could retieve it directly like the normal flow of things.

However, if you feel like the dynamic value shouldn’t be in the store then you can create a selector that returns a function with that dynamic value as an argument.

The gotcha here is that you would need to memoize this function on your own as Reselect selectors only have a cache of 1 and therefore couldn’t memoize your additional arguments. The normal way of providing memoization to subsequent arguments are to use the memoize function provided by Lodash. This example below is also straight from the docs — which you should really check out!

Summary

What are selectors: Selectors are functions that can be used to retrieve and/or derive data from a central data store. You can create selectors independent of any library.

Why use selectors: Selectors encapsulate knowledge of where data lives and how to derive it, and therefore can help you write more reusable code.

Why use Reselect: Reselect offers a performance enhancement as it provides a way to create selectors that are memoized and only recompute when their inputs have changed. This is beneficial in complex apps that are structured to have a minimal store and therefore rely on selectors to compute derived data (such as filtering, reducing, etc) which can be expensive operations.

Additional Resources and Further Reading

  1. Reselect’s Docs
  2. Computing Derived Data (Redux Docs)
  3. React, Reselect and Redux

--

--