Credit: Jackson Sophat

Need help with state management in your growing React application? Fear not! In this article, we’ll dive into the world of Redux and Context API, two powerful tools to help you streamline your state management. By the end, you’ll have a clear understanding of their differences, and when to use each one.

What is Redux?

Redux is a state management library that centralizes your application’s state in a single store and employs a unidirectional data flow.

Core Principles of Redux

  1. Single Source of Truth: Your application’s state is stored in a single object tree within a single store.
  2. State is Read-Only: The only way to change the state is by dispatching actions, plain JavaScript objects that describe what happened.
  3. Changes are Made with Pure Functions: Reducers specify how the state tree is transformed by actions, ensuring predictability.

Redux can be used with React and other frameworks like Angular and Vue.

Example: Setting Up Redux

Store Setup:

import { createStore } from 'redux';

const initialState = {
  todos: []
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, action.payload] };
    case 'REMOVE_TODO':
      return { ...state, todos: state.todos.filter(todo => todo.id !== action.payload) };
    default:
      return state;
  }
};

const store = createStore(reducer);

export default store;

React Component with Redux:

import React, { useState } from 'react';
import { Provider, useSelector, useDispatch } from 'react-redux';
import store from './store';

const addTodo = (todo) => ({ type: 'ADD_TODO', payload: todo });
const removeTodo = (id) => ({ type: 'REMOVE_TODO', payload: id });

const TodoApp = () => {
  const todos = useSelector(state => state.todos);
  const dispatch = useDispatch();
  const [task, setTask] = useState('');

  const handleAddTodo = () => {
    dispatch(addTodo({ id: Date.now(), task }));
    setTask('');
  };

  return (
    <div>
      <input
        type="text"
        value={task}
        onChange={(e) => setTask(e.target.value)}
        placeholder="Add a new task"
      />
      <button onClick={handleAddTodo}>Add</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            {todo.task}
            <button onClick={() => dispatch(removeTodo(todo.id))}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

const App = () => (
  <Provider store={store}>
    <TodoApp />
  </Provider>
);

export default App;

Data Flow in Redux

Diagram Created by author

Real-World Examples

Redux Example: E-commerce Application

Scenario: You are building an e-commerce application where you need to manage the state of a shopping cart, user authentication, and product listings.

Use Case for Redux:

  • Shopping Cart: Redux is ideal for managing the state of the shopping cart because it requires a centralized store to track items added, removed, and updated across different components (product list, cart summary, checkout page).
  • User Authentication: Managing authentication state globally ensures that the user’s login status is consistent throughout the app.
  • Product Listings: Redux can handle the fetching, storing, and updating of product data, allowing components to access the latest product information.

What is Context API?

Context API, built into React, allows you to pass data through the component tree without manually passing props at every level. It consists of the Context object and Provider/Consumer components.

Example: Setting Up Context API

Context Provider Setup:

import React, { createContext, useState } from 'react';

export const TodoContext = createContext();

const TodoProvider = ({ children }) => {
  const [todos, setTodos] = useState([]);

  const addTodo = (task) => {
    setTodos([...todos, { id: Date.now(), task }]);
  };

  const removeTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };

  return (
    <TodoContext.Provider value={{ todos, addTodo, removeTodo }}>
      {children}
    </TodoContext.Provider>
  );
};

export default TodoProvider;

React Component with Context API:

import React, { useContext, useState } from 'react';
import TodoProvider, { TodoContext } from './TodoProvider';

const TodoApp = () => {
  const { todos, addTodo, removeTodo } = useContext(TodoContext);
  const [task, setTask] = useState('');

  const handleAddTodo = () => {
    addTodo(task);
    setTask('');
  };

  return (
    <div>
      <input
        type="text"
        value={task}
        onChange={(e) => setTask(e.target.value)}
        placeholder="Add a new task"
      />
      <button onClick={handleAddTodo}>Add</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            {todo.task}
            <button onClick={() => removeTodo(todo.id)}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

const App = () => (
  <TodoProvider>
    <TodoApp />
  </TodoProvider>
);

export default App;

Data Flow in Context API

Diagram Created by author

Real-World Example: Theme Management

Picture yourself building a blog website where users can toggle between light and dark themes. Context API is suitable for this case as the state (theme) is relatively simple and only needs to be shared across a few components (header, footer, main content).

When to Use Redux

  • Large Applications: For complex apps with extensive state management needs, Redux shines with its robust architecture.
  • Debugging: Redux DevTools provide powerful debugging capabilities, making it easier to track state changes.
  • Scalability: Redux’s unidirectional data flow and middleware support make it a great choice for scalable applications.

When to Use Context API

  • Small to Medium-Sized Applications: Ideal for apps with simpler state management needs.
  • Ease of Use: Built into React, Context API is easier to set up and use for straightforward state management.
  • Reduced Boilerplate: Less setup and boilerplate code compared to Redux, making it more accessible for beginners.

Categorized in: