How to Update Redux Selectors in React
This article provides a direct guide on how to update and refactor Redux selectors in React applications. You will learn how to transition from basic selector functions to optimized, memoized selectors using Redux Toolkit, how to update selectors to handle dynamic arguments, and how to ensure your React components re-render efficiently when the Redux state updates.
Understanding the Need to Update Selectors
Selectors are functions that extract specific pieces of data from the Redux store. As your application grows, basic inline selectors can cause unnecessary component re-renders because they return new object references every time they run. Updating your selectors to use memoization ensures that components only re-render when the underlying data actually changes.
Step 1: Upgrading Basic Selectors to Memoized Selectors
Basic selectors simply retrieve state. When updating these to handle
complex data transformations (like filtering or mapping), you should
update them using createSelector from Redux Toolkit (which
integrates Reselect).
Before: A Basic, Non-Memoized Selector
// This runs on every state change and returns a new array reference
const selectCompletedTodos = (state) => {
return state.todos.filter(todo => todo.completed);
};After: Updated Memoized Selector
To update this selector for optimal performance, wrap it with
createSelector. It will now only recalculate if
state.todos changes.
import { createSelector } from '@reduxjs/toolkit';
const selectTodos = (state) => state.todos;
export const selectCompletedTodos = createSelector(
[selectTodos],
(todos) => todos.filter(todo => todo.completed)
);Step 2: Updating Selectors to Accept Dynamic Arguments
Sometimes you need to update a selector so it can accept arguments from a component, such as an ID or a search query.
To do this, update your selector definition to accept input
parameters, and pass those parameters through the
useSelector hook in your React component.
Defining the Parameterized Selector
import { createSelector } from '@reduxjs/toolkit';
const selectTodos = (state) => state.todos;
const selectTodoId = (state, todoId) => todoId;
export const selectTodoById = createSelector(
[selectTodos, selectTodoId],
(todos, todoId) => todos.find(todo => todo.id === todoId)
);Using the Updated Selector in a Component
Pass the dynamic argument inside the useSelector
hook.
import { useSelector } from 'react-redux';
import { selectTodoById } from './todoSelectors';
const TodoItem = ({ id }) => {
// Pass the state and the dynamic id to the selector
const todo = useSelector((state) => selectTodoById(state, id));
return <div>{todo?.text}</div>;
};Step 3: Handling State Updates with useSelector
When the Redux state is updated via dispatched actions, React Redux automatically runs your selectors.
If you are updating a selector that returns a new object or array,
you must ensure you use a shallow equality check to prevent infinite
loops or unnecessary renders. Update your component’s
useSelector hook by passing shallowEqual as
the second argument:
import { useSelector, shallowEqual } from 'react-redux';
import { selectUserMetadata } from './userSelectors';
const UserProfile = () => {
// Use shallowEqual when the updated selector returns a new object
const userMetadata = useSelector(selectUserMetadata, shallowEqual);
return <div>{userMetadata.name}</div>;
};