Redux

Ultimate Guide to React Redux

February 02, 2026
admin1

Understanding React Redux: A Complete Guide

Introduction to Redux

Redux is a predictable state container for JavaScript applications. It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. Redux is commonly used with React, but it can be used with any JavaScript framework or library.

The Problem Redux Solves

Props Drilling Hell

In React applications, as your component tree grows, passing data through multiple levels of components becomes cumbersome. This is known as "props drilling."

// Without Redux - Props Drilling
<C1>
  <C2>
    <C3>
      <C4 data={data} />
    </C3>
  </C2>
</C1>

State Lifting Problem

When multiple components need to share the same state, you need to lift the state to the common ancestor. This can lead to:

  • Accidental changes - State can be modified unintentionally
  • Repeated functions - Same function used in many components
  • Global state issues - Only one store for unique functions and variables

Redux Core Concepts

1. Store

The store is the single source of truth for your application state. It holds the entire state tree of your application.

import { createStore } from 'redux';

const store = createStore(reducer);

2. Actions

Actions are plain JavaScript objects that describe what happened. They must have a type property.

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

// Action creators
function increment() {
  return { type: INCREMENT };
}

function decrement() {
  return { type: DECREMENT };
}

3. Reducers

Reducers are pure functions that take the current state and an action, and return a new state.

function counterReducer(state = { count: 0 }, action) {
  switch (action.type) {
    case INCREMENT:
      return { count: state.count + 1 };
    case DECREMENT:
      return { count: state.count - 1 };
    default:
      return state;
  }
}

4. Dispatch

Dispatch is a function that sends actions to the store to update the state.

store.dispatch(increment());
store.dispatch(decrement());

The Redux Flow

  1. Component calls an action
  2. Action is dispatched to the store
  3. Reducer processes the action and returns new state
  4. Store updates with new state
  5. Component re-renders with updated data

Context API vs Redux

When to Use Context API

  • Small to medium applications
  • Simple state management
  • Few state updates
  • Limited global state

When to Use Redux

  • Large applications
  • Complex state logic
  • Frequent state updates
  • Need for middleware (logging, async operations)
  • Time-travel debugging
  • State persistence

Redux Slices (Redux Toolkit)

Modern Redux uses Redux Toolkit which simplifies the setup:

import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { count: 0 },
  reducers: {
    increment: (state) => {
      state.count += 1; // Immer allows "mutation"
    },
    decrement: (state) => {
      state.count -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;

Solving the Amazon Store Example

Imagine building a large e-commerce site like Amazon where many people work on the same store. You need to:

  1. Divide the store into slices (cart, user, products, etc.)
  2. Each slice manages its own state
  3. Components can access any slice
  4. Dispatch automatically sends actions to the correct slice
// Store structure
{
  cart: { items: [], total: 0 },
  user: { name: '', isLoggedIn: false },
  products: { list: [], filters: {} }
}

Best Practices

1. Keep Reducers Pure

Never mutate state directly. Always return a new state object.

2. Use Action Creators

Encapsulate action creation logic in functions.

3. Normalize State Shape

Store data in a normalized form to avoid duplication.

4. Use Redux DevTools

Enable time-travel debugging and state inspection.

5. Consider Redux Toolkit

Use Redux Toolkit for simpler, more maintainable code.

Common Patterns

Async Operations with Redux Thunk

const fetchUser = (userId) => {
  return async (dispatch) => {
    dispatch({ type: 'FETCH_USER_REQUEST' });
    
    try {
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      dispatch({ type: 'FETCH_USER_SUCCESS', payload: data });
    } catch (error) {
      dispatch({ type: 'FETCH_USER_FAILURE', error });
    }
  };
};

Selectors for Derived Data

import { createSelector } from 'reselect';

const getCart = (state) => state.cart;

const getCartTotal = createSelector(
  [getCart],
  (cart) => cart.items.reduce((total, item) => total + item.price, 0)
);

Setting Up Redux in React

1. Install Dependencies

npm install @reduxjs/toolkit react-redux

2. Create Store

// store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

3. Provide Store to App

// index.js
import { Provider } from 'react-redux';
import { store } from './store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

4. Use in Components

import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';

function Counter() {
  const count = useSelector((state) => state.counter.count);
  const dispatch = useDispatch();

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
}

Performance Optimization

1. Use Memoization

Use useMemo and useCallback to prevent unnecessary re-renders.

2. Selective Subscription

Only subscribe to the parts of state you need.

// Good - only subscribes to count
const count = useSelector((state) => state.counter.count);

// Bad - subscribes to entire counter slice
const counter = useSelector((state) => state.counter);

3. Batch Updates

Redux automatically batches updates in React event handlers.

Common Mistakes to Avoid

  1. Mutating State - Always return new objects
  2. Too Much in Redux - Not all state needs to be global
  3. Not Using Redux Toolkit - It simplifies everything
  4. Ignoring DevTools - They're incredibly useful
  5. Over-engineering - Start simple, add complexity when needed

Conclusion

Redux provides a robust solution for managing complex application state. While it adds some boilerplate, the benefits of predictable state management, time-travel debugging, and middleware support make it invaluable for large applications.

Key Takeaways

  • Redux solves props drilling and state management issues
  • Core concepts: Store, Actions, Reducers, Dispatch
  • Use Redux Toolkit for modern Redux development
  • Not every application needs Redux - evaluate your needs
  • Follow best practices for maintainable code

When to Use Redux

✅ Large applications with complex state
✅ State shared across many components
✅ Frequent state updates
✅ Need for middleware and debugging tools

❌ Simple applications
❌ Local component state
❌ Learning React basics

Further Reading


Happy Coding! 🚀

Remember: Redux is a tool, not a requirement. Use it when it makes your application better, not just because it's popular.

Tags

Reduxthe problem redux solveredux core concepts