What are Redux selectors?

Key takeaways:

  • Redux is a library that provides a predictable state container for JavaScript applications.

  • Selectors are functions that extract and derive data from the Redux store.

  • The selectors provide functionality for data extraction, encapsulation, and reusability.

  • Memoized selectors cache results to prevent unnecessary recalculations and improve performance.

  • We can use selectors with hooks like useSelector to access state within React components efficiently.

When it comes to state management in JavaScript, Redux stands as a popular and powerful library.

It provides a predictable state container for JavaScript applications, making it easier to manage and track changes to the state. However, accessing and deriving data from the Redux store can become increasingly complex as the application grows.

This is where Selectors come into play.

What are selectors?

Selectors are functions that take the Redux state as input and return some derived state or data. They provide an abstraction layer between the state and the components, making our code cleaner and more maintainable. Selectors can be used to:

  • Extract specific pieces of state: Imagine we have an application with a complex state that includes user information, settings, and other data. A selector can help by extracting just the user information you need for a profile component, ignoring the rest.

  • Derive data from the state: In an e-commerce application, we might need to show a list of items that are on sale. A selector can derive this data by filtering the full product list, providing only the items with a discount.

  • Memoize derived data to avoid unnecessary re-renders: Consider a task management app where we need to display the list of completed tasks. By memoizing this derived data, the app can avoid recalculating the completed tasks list every time the state changes, improving performance and preventing unnecessary updates to the UI.

Why should we use selectors?

Let’s see why we use selectors:

  1. Encapsulation: Selectors encapsulate the logic for accessing the state. This makes it easier to change the state shape without impacting the components that consume the state.

  2. Reusability: Once defined, selectors can be reused across different components.

  3. Performance optimization: Memoized selectors can improve the performance of the application by preventing unnecessary re-renders.

  4. Testability: Selectors can be independently tested, which ensures that the data extraction logic is correct.

Benefits of using selectors

The following are the benefits of Selectors:

  1. Simplified state access: Components can access the state without worrying about its structure.

  2. Separation of concerns: Keeps state access logic separate from component logic.

  3. Improved performance: Memoized selectors prevent unnecessary computations and re-renders.

  4. Enhanced maintainability: Easier to manage and refactor state access logic.

Selectors creation

Let’s start with a basic example to illustrate how to create and use selectors in a Redux application.

Step 1: Setting up Redux

First, let’s set up a simple Redux store.

import { createStore } from 'redux';
// Initial state
const initialState = {
users: [
{ id: 1, name: 'Dexter', age: 25 },
{ id: 2, name: 'Sara', age: 30 },
],
};
// Reducer function
function userReducer(state = initialState, action) {
switch (action.type) {
default:
return state;
}
}
// Create Redux store
const store = createStore(userReducer);
export default store;

Let’s see the working of the above code:

  • Line 1: Import the createStore function from the redux library to create a Redux store.

  • Lines 4–9: Define the initialState object:

    • Line 4: Initialize a users array within the state.

    • Lines 5–8: Populate the users array with two user objects, each containing an id, name, and age property.

  • Line 12: Define the userReducer function with state and action parameters, setting the default state to initialState.

  • Line 13: Use a switch statement to handle different action types.

  • Line 14: Add a default case in the switch statement to return the current state unchanged if the action type doesn’t match any cases.

  • Line 15: Return the current state as the default case of the switch statement.

  • Line 20: Create the Redux store using createStore and pass the userReducer to it.

  • Line 22: Export the store as the default export to make it available for import in other parts of the application.

Step 2: Defining basic selectors

Next, we define some basic selectors to access the state.

export const getUsers = (state) => state.users;
export const getUserById = (state, userId) => state.users.find(user => user.id === userId);

In the above code snippet:

  • Line 1: The getUsers function is defined and exported. It takes the state object as an argument and returns the users array from the state. This function acts as a selector to retrieve all users from the Redux state.

  • Line 3: The getUserById function is defined and exported. It takes the state object and a userId as arguments. The function uses the find method on the users array in the state to locate and return the user whose id matches the provided userId. If no matching user is found, it returns undefined. This function acts as a selector to retrieve a specific user by their ID from the Redux state.

Step 3: Selectors in components

Now, let’s use these selectors in a React component.

import React from 'react';
import { useSelector } from 'react-redux';
import { getUsers, getUserById } from './selectors';
const UserList = () => {
const users = useSelector(getUsers);
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name} - {user.age} years old</li>
))}
</ul>
);
};
export default UserList;

Here’s the breakdown of the above code:

  • Line 2: Import the useSelector hook from the react-redux library, which is used to extract data from the Redux store.

  • Line 3: Import the getUsers selector from the ../selectors/selectors file, which will be used to select the list of users from the Redux store.

  • Lines 5–6: Define a functional component named UserList and use the useSelector hook to access the Redux store and select the list of users using the getUsers selector. The selected users are stored in the users constant.

  • Lines 9–13: Iterate over the users array using the map method. For each user, render an li element with a key attribute set to the user’s id to ensure each list item is uniquely identified and displays the user's name and age within the li element.

Step 4: Memoized selectors

For larger and more complex applications, using memoized selectors can significantly improve performance. The reselect library is commonly used for this purpose.

// memoizedSelectors.js
import { createSelector } from 'reselect';
const selectUsers = (state) => state.users;
export const getUsers = createSelector(
[selectUsers],
(users) => users
);
export const getUserById = createSelector(
[selectUsers, (state, userId) => userId],
(users, userId) => users.find(user => user.id === userId)
);

Let’s see the breakdown of the above code:

  • Line 2: Import the createSelector function from the reselect library. The reselect is a library for creating memoized selectors, which helps in optimizing performance by avoiding unnecessary recalculations.

  • Line 4: Define a basic selector function named selectUsers. This function takes the Redux state as an argument and returns the users array from the state.

  • Lines 6–9: Define and export a memoized selector named getUsers using createSelector.

    • Line 7: createSelector takes an array of input selectors as its first argument. In this case, it is [selectUsers], which is a single input selector that retrieves the users array from the state.

    • Line 8: The second argument is a transformation function that receives the results of the input selectors as arguments. Here, it simply returns the users array.

The getUsers selector is now memoized, meaning it will only recompute the users array if the users array in the state changes.

  • Lines 11–14: Define and export a memoized selector named getUserById using createSelector. createSelector takes an array of input selectors as its first argument. Here, it has two input selectors:

    • Line 12: selectUsers, which retrieves the users array from the state, and an inline selector (state, userId) => userId, which simply returns the userId argument.

    • Line 13: The second argument is a transformation function that receives the results of the input selectors (users and userId) as arguments. It uses the find method on the users array to locate and return the user whose id matches the provided userId.

The getUserById selector is now memoized, meaning it will only recompute the result if the users array or the userId argument changes.

Step 5: Memoized selectors in components

Update the component to use memoized selectors.

import React from 'react';
import { useSelector } from 'react-redux';
import { getUserById } from './memoizedSelectors';
const UserDetails = ({ userId }) => {
const user = useSelector(state => getUserById(state, userId));
if (!user) {
return <div>User not found</div>;
}
return (
<div>
<h2>{user.name}</h2>
<p>Age: {user.age}</p>
</div>
);
};
export default UserDetails;

Here’s the breakdown of the above code:

  • Line 3: Import the getUserById selector from the ../selectors/memoizedSelectors file, which will be used to select a specific user from the Redux store by their ID.

  • Line 5: Define a functional component UserDetails which takes a userId prop.

  • Line 6: Use the useSelector hook to access the Redux store and select the user with the specified userId using the getUserById selector. The selected user is stored in the user constant.

  • Lines 8-10: Check if the user is not found (i.e., user is null or undefined). If the user is not found, return a div element displaying “User not found.”

  • Lines 12-17: If the user is found, return a div element containing:

    • An h2 element displaying the user’s name.

    • A p element displaying the user’s age.

Before proceeding and seeing all the above codes working together, let's have a brief overview of what we are trying to achieve.

In this example, we’re building a simple React and Redux application to manage user data.

The application consists of a list of users and detailed information about a selected user. The goal is to demonstrate how to use Redux selectors, including memoized selectors, with the reselect library to efficiently manage and access the state.

Let's see this in action!

Code explanation

We have already discussed most of the files already let’s jump to those that we have not observed earlier.

index.js file
  • Line 2: Import the createRoot function from react-dom/client to use the new React 18 rendering API.

  • Line 3: Import the Provider component from react-redux to make the Redux store available to the entire app.

  • Line 4: Import the Redux store from the specified path.

  • Line 5: Import the main App component.

  • Line 7: Create a root to render the app using createRoot.

  • Lines 9–12: Wrap the App component with Provider to pass the Redux store to the entire app and render the main App component.

App.js file
  • Line 6: Use a JSX div element as the container for the component’s content.

  • Line 7: Include an h1 element with the text “User Management” to serve as the main heading.

  • Line 8: Render the UserList component, which will display a list of users.

  • Line 9: Render the UserDetails component, passing a userId prop with the value 1 to display details of the user with id 1.

Now, it’s time to understand how selector played its role:

Encapsulating state access logic

  • Selectors: The selectors.js file contains simple selector functions (getUsers and getUserById) that encapsulate the logic for accessing specific parts of the state. This encapsulation ensures that components do not directly access the state, promoting better separation of concerns and making the state access logic reusable and easier to maintain.

Leveraging memoization

  • Memoized selectors: The memoizedSelectors.js file uses the reselect library to create memoized selectors. These selectors are more efficient because they remember the results of the previous computations and return the cached result if the input state has not changed. This reduces unnecessary recomputations and re-renders, enhancing the performance of the application.

    • createSelector: This function from reselect creates memoized selectors. For example, getUserById memoizes the result based on the state and the userId, ensuring that the expensive computation (finding a user by ID) is only performed when necessary.

Conclusion

Redux selectors are an essential tool for any Redux-based application.

They provide a clean and efficient way to access and derive state, leading to more maintainable and performant code. Selectors can significantly enhance the application’s architecture by encapsulating state access logic and leveraging memoization.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved