What is a Redux selector?

Matthew Holman
5 min readJul 4, 2018

--

I was tasked with helping to build out the Redux architecture of a single page application at work. I have built plenty of projects with Redux, but never had to seriously consider performance at scale until this application. As I began doing some research, I repeatedly ran across “selectors”, but had no clue what they were. This article will answer that question for you and hopefully help you determine if they are appropriate for your project.

A “selector” is simply a function that accepts Redux state as an argument and returns data that is derived from that state. Bam! The end.

No no no. That’s not the end.

Why should you use a selector?

It is a best practice to keep your Redux store state minimal and derive data from the state as needed. Selectors help with that. They can compute derived data, allowing Redux to store the minimal possible state. Selectors are also very efficient. A selector is not recomputed unless one of its arguments changes.

If selectors are so useful, why haven’t I heard of them before?

The Redux documentation doesn’t talk much about selectors as a core concept. However, the FAQ in the Redux documentation states:

It’s generally suggested that selectors are defined alongside reducers and exported, and then reused elsewhere (such as in mapStateToProps functions, in async action creators, or sagas) to colocate all the code that knows about the actual shape of the state tree in the reducer files.

Selectors are also mentioned in the official Redux docs section on Computing Derived Data. They are covered more thoroughly in Dan Abramov’s second series of Redux videos, particularly in Redux: Co-locating Selectors with Reducers.

A few examples…

This is definitely not a selector.

We have discussed the merits of selectors enough, let’s finally dive into a few examples! Below is a simple selector function for retrieving a list of users from the store:

selectUsers = state => state.users;

We can make the above selector slightly more complex by getting user IDs:

selectUserIds = state => state.users.map(user => user.id);

And just to drive the point home, below is an even more complex selector to select users with a provided name:

selectUserIdsOfName = (state, name) => state.users.filter(user => user.name === name);

Best practices…

It is a best practice to give your selector function a name that contains “selector”. I prefer to use ‘select’ as a prefix, but it’s also common to use ‘Selector’ as a suffix (i.e. userIdsSelector instead of selectUserIds).

It is also common practice to keep your selector near the appropriate reducer. That way, if you change the shape of the state, it is easy to remember to change your selector as well.

Below is an example of a selector bundled with a reducer. We’ll use the reducer example from the official Redux docs:

// Reducer
function todoApp(state = [], action) {
switch (action.type) {
case GET_ALL_TODOS:
return {
todos: action.data.todos
};
default:
return state;
}
}
// Selector
function getIncompleteTodos(state) {
return state.todos.filter((todo) => {
return !todo.completed
});
}

Now, instead of using something like the following in a smart component’s mapStateToProps() function:

function mapStateToProps(state) {
return {
incompleteTodos: state.todos.filter((todo) => {
return !todo.completed
});
}
}

We could use the newly created selector in its place:

function mapStateToProps(state) {
return {
incompleteTodos: getIncompleteTodos(state)
};
}

The code is more concise and we have made a big step toward encapsulating our state tree. The component no longer cares how todos are stored in the state tree and it will be much easier to refactor if the state tree needs to be updated!

Memoization

The use of selectors in your application can also provide performance optimizations. Let’s say you have a component that needs to run an intensive sorting operation on the store’s state in order to get the data it needs. If you were to perform the operation in your mapStateToProps() function, without the use of a selector, the operation would run every time a dispatched action caused the state to update!

It would be great if we could only run the expensive sorting operation only when the data we are running the operation on changes. This is where the concept of memoization comes to the rescue.

Memoization is a form of caching. It involves tracking inputs to a function, and storing the inputs and the results for later reference. If a function is called with the same inputs as before, the function can skip doing the actual work, and return the same result it generated the last time it received those input values.

The official Redux docs briefly discuss memoization. There is also an amazing Redux middleware library for creating memoized selectors called Reselect:

Reselect

Redux Reselect middleware to the rescue!

We have been creating our own selectors in the examples above, but there is an excellent library out there called Reselect that helps with creating memoized selectors.

I won’t dive in to the specifics of using Reselect in this article, but I strongly encourage you to check out the official Reselect docs! In addition to the official docs, I ran across a superb article that dives into using the Reselect library and how to further optimize your React-Redux applications with Reselect.

Recap

TL;DR

A selector is a function that accepts Redux state as an argument and returns data that is derived from that state. Selectors can provide performance optimizations to your application and can also help you encapsulate your global state tree. If your application is growing large and unwieldy, you may benefit from creating your own selectors or using a library such as Reselect, which utilizes memoized selectors.

Additional Resources

--

--