useSelector: Reading Redux State in React Components

useSelector is the hook that reads from the Redux store and triggers a re-render when that slice of state changes. Learn why it exists and how to use it to render the task list.

June 27, 20264 min read1 / 3

The store is created and Provider is in place. The Redux state is initialized. What does not exist yet: any connection between that state and the task cards in the UI.

Two ways to read state exist. One is wrong for React. The other is useSelector.

Why Not store.getState() Directly?

You can import the store file and call store.getState() anywhere:

JavaScript
import store from '../../store'; const tasks = store.getState().tasks;

This works once. The problem: the component does not re-render when state changes. React does not know the store exists. When a new task is dispatched, tasks still holds the old value. The UI freezes.

useSelector solves this. It subscribes the component to the Redux store. Every time the reducer returns a new state, any component that read that slice automatically re-renders with the fresh value.

Basic Usage

JSX
import { useSelector } from 'react-redux'; const tasks = useSelector((state) => state.tasks);

The argument is a selector function. Redux calls it with the current state and the return value becomes the hook's return value. The property name tasks matches the key we gave in combineReducers, not the reducer function name.

Any name for the parameter works. Some teams use store instead of state. It does not matter, it represents the full Redux state object.

Rendering the Task List Dynamically

In Tasks.js, replace the hardcoded cards with a dynamic map:

JSX
import { useSelector } from 'react-redux'; export default function Tasks() { const tasks = useSelector((state) => state.tasks); // ... toggle state and handlers ... return ( <div className="outer-container"> <div className="container"> {/* header, Collapsible form ... */} <div className="content-body"> {tasks.map((task) => ( <div key={task.id} className="task"> <div className="task-body"> <div className="task-title"> <i className="fas fa-tasks" /> <span>{task.taskTitle}</span> </div> <div className="task-subtitle">{task.dateTime}</div> </div> <div className="task-options"> <button className="icon-button">&times;</button> </div> </div> ))} </div> </div> </div> ); }

The key prop is task.id. The taskTitle and dateTime fields match the shape defined in the initial state.

Confirming in DevTools: open the Redux DevTools extension in Chrome and check the State tab. You should see tasks: [...] with the initial task objects. Add a new entry to initialTasks in data/tasks.js and the UI reflects it immediately on reload without touching any component code.

useSelector reading the tasks slice of Redux state ExpanduseSelector reading the tasks slice of Redux state

How Re-rendering Works

useSelector runs the selector after every dispatched action. If the selected value changed (by reference), React schedules a re-render. If the value did not change, the component stays as-is. This is why returning new objects on every render from the selector is wasteful. Always select a specific slice, not the whole state.

JavaScript
// correct: reads one slice const tasks = useSelector((state) => state.tasks); // wrong: creates a new object reference every render → re-renders every time const data = useSelector((state) => ({ tasks: state.tasks }));

A note for when you reach the async chapters: right now state.tasks is an array. When Redux Thunk is added in Chapter 3, the state shape changes to { data: [], loading: false, error: '' }. At that point tasks.map(...) becomes tasks.data.map(...). This is intentional: the initial version uses simple state, and it expands once async is introduced.

The Essentials

  1. useSelector(selector) subscribes the component to the Redux store. When the selected state changes, the component re-renders. store.getState() alone does not trigger re-renders.
  2. The selector parameter name is arbitrary. state.tasks references the key defined in combineReducers, not the reducer function name.
  3. Return the minimum slice needed. Selecting a narrow slice reduces unnecessary re-renders. A selector that returns a full object literal re-renders on every dispatch regardless of whether the data changed.

Further Reading and Watching

Practice what you just read.

Implement useSelector
1 exercise