What are Selectors?
At their most simple, Selectors are simply functions that are used to select a subset data from a larger data collection.
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.
1) Have knowledge of the whereabouts or path to find a particular subset of data and
2) Return with the requested subset of data
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
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
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 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:
- Selector(s) — if you have multiple selectors they can be inputted as a comma separated (like shown above) or as an array.
- A tranformer function that takes the values of the selectors from the first arguments and uses them to select or derive relevant data
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.
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.
To cut down on boilerplate, Reselect offers a
createStructuredSelector that can be used in place of a
creatSelector 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
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
mapStateToPropsargument supplied to
connectreturns a function instead of an object, it will be used to create an individual
mapStateToPropsfunction for each instance of the container.” (x)
To accomplish this in practice:
- 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
And that’s it!
Passing an argument that’s a static value:
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!
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.