# Redux useSelector Hook Best Practices for React Performance and Maintainability

> Optimize React performance with Redux useSelector hook best practices. Learn to memoize state, write pure selectors, and use shallow checks for efficient updates.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: best-practices
- Published: 2026-02-16

---

**Use the Redux `useSelector` hook to subscribe components to minimal, memoized state slices, keep selectors pure and outside component bodies, and leverage shallow equality checks to eliminate unnecessary re-renders.**

The `useSelector` hook from the **react-redux** library (the official Redux bindings for React, maintained separately from the core `facebook/react` repository) enables components to extract data from the Redux store and subscribe to state updates. While powerful, improper usage leads to performance bottlenecks and unmaintainable code. This guide covers architectural patterns derived from the hook's implementation in [`src/hooks/useSelector.ts`](https://github.com/facebook/react/blob/main/src/hooks/useSelector.ts) and community-vetted practices for production applications.

## How useSelector Works Under the Hood

Understanding the internal mechanics informs optimization decisions. According to the react-redux source code:

1. **Subscription** – The hook subscribes the component to the Redux store. When any action dispatches, the store notifies all subscribers.
2. **Selector Execution** – The provided selector function runs with the latest state to extract the desired value.
3. **Equality Comparison** – React-Redux compares the current result with the previous result using strict equality (`===`) by default, or a custom comparison function.
4. **Re-render Trigger** – If the values differ, React schedules a re-render.

This process relies on [`src/utils/shallowEqual.ts`](https://github.com/facebook/react/blob/main/src/utils/shallowEqual.ts) for the optional shallow equality check and [`src/components/Provider.tsx`](https://github.com/facebook/react/blob/main/src/components/Provider.tsx) to supply the store context.

## Performance Optimization Strategies

### Select Minimal State Slices

React re-renders the component whenever the selector's return value changes. Selecting more state than necessary forces unnecessary renders when unrelated data updates.

```javascript
// ❌ Bad: Subscribes to entire user object
const user = useSelector(state => state.user);

// ✅ Good: Select only the specific property needed
const userName = useSelector(state => state.user.name);

```

### Memoize Complex Selectors with Reselect

Deriving data through complex calculations inside `useSelector` runs on every render. Use memoized selectors from **Redux Toolkit** to cache results until inputs change.

```typescript
// selectors.ts
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from './store';

const selectItems = (state: RootState) => state.items;
const selectFilter = (state: RootState, filterType: string) => filterType;

export const selectVisibleItems = createSelector(
  [selectItems, selectFilter],
  (items, filter) => items.filter(item => item.type === filter)
);

// Component.tsx
import { selectVisibleItems } from './selectors';

function ItemList({ filter }: { filter: string }) {
  const visible = useSelector(state => selectVisibleItems(state, filter));
  return <ul>{visible.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}

```

### Optimize Equality Checks

By default, `useSelector` uses strict equality. For objects or arrays, import `shallowEqual` from `react-redux` to compare values instead of references.

```javascript
import { useSelector, shallowEqual } from 'react-redux';

const settings = useSelector(
  state => state.settings,
  shallowEqual  // Prevents re-render if only reference changes
);

```

The `shallowEqual` function is implemented in [`src/utils/shallowEqual.ts`](https://github.com/facebook/react/blob/main/src/utils/shallowEqual.ts) and performs a shallow comparison of object keys and values.

### Batch Related Data Selections

Multiple `useSelector` calls create separate subscriptions. Group related data into a single selector to reduce overhead.

```javascript
// ❌ Inefficient: Three separate subscriptions
const name = useSelector(state => state.user.name);
const email = useSelector(state => state.user.email);
const avatar = useSelector(state => state.user.avatar);

// ✅ Efficient: Single subscription
const { name, email, avatar } = useSelector(state => state.user);

```

## Maintainability Patterns

### Keep Selectors Pure and Side-Effect-Free

`useSelector` runs during the render phase. Side effects inside selectors execute repeatedly and cause unpredictable behavior.

```javascript
// ❌ Bad: Side effect inside selector
const count = useSelector(state => {
  console.log('Selecting:', state.count); // Runs every render!
  return state.count;
});

// ✅ Good: Pure function
const count = useSelector(state => state.count);

```

### Define Selectors Outside Components

Declaring selectors inside components creates new function references every render, breaking memoization. Define them in separate modules.

```javascript
// selectors.js
import { createSelector } from '@reduxjs/toolkit';

export const selectUserById = createSelector(
  [state => state.users, (_, userId) => userId],
  (users, userId) => users[userId]
);

// Component.js
import { selectUserById } from './selectors';

function UserProfile({ userId }) {
  const user = useSelector(state => selectUserById(state, userId));
  return <div>{user.name}</div>;
}

```

### Maintain Single Responsibility

Avoid deeply nested selector logic. Compose small, focused selectors.

```javascript
// Good: Composable, single-purpose selectors
const selectPosts = state => state.posts;
const selectPostIds = createSelector([selectPosts], posts => posts.map(p => p.id));
const selectPostById = createSelector(
  [selectPosts, (_, id) => id],
  (posts, id) => posts.find(p => p.id === id)
);

```

### Separate Local and Global State

Use Redux for shared global state only. UI-local state belongs in `useState`.

```javascript
import { useState } from 'react';
import { useSelector } from 'react-redux';

function SearchableList() {
  const [query, setQuery] = useState(''); // Local UI state
  const items = useSelector(state => state.items); // Global state
  
  const filtered = items.filter(item => item.includes(query));
  
  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <ul>{filtered.map(i => <li key={i}>{i}</li>)}</ul>
    </>
  );
}

```

## Type Safety and Testing

### Implement TypeScript Types for Selectors

Strong typing catches errors at compile time. Define a `RootState` type and apply it consistently.

```typescript
// store.ts
import { configureStore } from '@reduxjs/toolkit';

export const store = configureStore({
  reducer: { user: userReducer, posts: postsReducer }
});

export type RootState = ReturnType<typeof store.getState>;

// Component.tsx
import { useSelector } from 'react-redux';
import type { RootState } from './store';

function UserProfile() {
  const theme = useSelector((state: RootState) => state.ui.theme);
  return <div className={theme}>Content</div>;
}

```

### Unit Test Selectors Independently

Test selectors as pure functions without rendering components.

```javascript
// selectors.test.js
import { selectVisibleItems } from './selectors';

test('filters items by type', () => {
  const state = { 
    items: [
      { id: 1, type: 'active', name: 'Task 1' },
      { id: 2, type: 'completed', name: 'Task 2' }
    ] 
  };
  
  expect(selectVisibleItems(state, 'active')).toEqual([
    { id: 1, type: 'active', name: 'Task 1' }
  ]);
});

```

## Key Implementation Files

While the `facebook/react` repository contains the core React engine, the `useSelector` hook resides in the **react-redux** repository. These source files govern its behavior:

| File | Role | Source |
|------|------|--------|
| [`src/hooks/useSelector.ts`](https://github.com/facebook/react/blob/main/src/hooks/useSelector.ts) | Core hook implementation handling subscriptions, selector execution, and equality comparisons. | [View Source](https://github.com/reduxjs/react-redux/blob/main/src/hooks/useSelector.ts) |
| [`src/utils/shallowEqual.ts`](https://github.com/facebook/react/blob/main/src/utils/shallowEqual.ts) | Shallow equality algorithm used when the `shallowEqual` comparator is provided. | [View Source](https://github.com/reduxjs/react-redux/blob/main/src/utils/shallowEqual.ts) |
| [`src/components/Provider.tsx`](https://github.com/facebook/react/blob/main/src/components/Provider.tsx) | React Context provider that supplies the Redux store to `useSelector` via context. | [View Source](https://github.com/reduxjs/react-redux/blob/main/src/components/Provider.tsx) |

## Summary

- **Select minimal state slices** to prevent re-renders when unrelated state changes occur.
- **Memoize derived data** using `createSelector` from Redux Toolkit to cache expensive computations.
- **Use `shallowEqual`** when selecting objects or arrays to avoid re-renders from reference changes.
- **Keep selectors pure** and free of side effects; they execute during the render cycle.
- **Define selectors outside components** to maintain stable references and enable effective memoization.
- **Separate local and global state**; use `useState` for UI-local concerns and Redux for shared state.
- **Batch related data** into single selectors to reduce subscription overhead.
- **Type selectors with `RootState`** to enforce compile-time safety and improve maintainability.
- **Test selectors independently** as pure functions for fast, reliable unit tests.

## Frequently Asked Questions

### Why does my component re-render even when the selected data hasn't changed?

By default, `useSelector` uses strict equality (`===`) to compare the previous and current selector results. If you select an object or array, Redux may return a new reference on every dispatch even if the contents are identical. Import `shallowEqual` from `react-redux` and pass it as the second argument to compare values instead of references, or refactor your selector to return primitive values.

### Should I use useSelector for every piece of state my component needs?

No. Use `useSelector` only for global Redux state that multiple components share. For UI-local state such as form inputs, toggle flags, or animation states, use React's built-in `useState` hook. This keeps your Redux store lean, reduces selector complexity, and improves performance by minimizing subscription overhead and preventing unnecessary global state updates.

### How do I optimize expensive calculations in useSelector?

Move complex derivations such as filtering large lists, sorting, or aggregating data out of inline `useSelector` callbacks and into memoized selectors using `createSelector` from Redux Toolkit. Memoized selectors cache results and only recalculate when their input values change, preventing expensive operations from running on every component render regardless of whether the underlying data changed.

### Where should I define my selector functions?

Define selectors at module scope in separate files (e.g., [`selectors.ts`](https://github.com/facebook/react/blob/main/selectors.ts)) or alongside your Redux slice definitions, never inside component bodies. Creating selector functions inside components generates new function references on every render, which breaks memoization libraries like Reselect and can cause infinite re-render loops. Module-level selectors maintain stable references, enable reuse across components, and support proper memoization caching.