Managing Global State with Redux in React

Explore practical examples of managing global state with Redux in React for efficient state management.
By Jamie

Introduction

Managing global state in a React application can be challenging, especially as the app scales. Redux is a powerful library that provides a predictable state container, allowing you to manage complex state interactions efficiently. In this article, we will explore three diverse examples of managing global state with Redux in React, helping you understand how to implement Redux effectively in your applications.


Example 1: Counter Application

Context

A simple counter application is a great way to illustrate how Redux can manage global state. This example will show how to increment and decrement a counter value across components.

// actions.js
export const increment = () => ({ type: 'INCREMENT' });
export const decrement = () => ({ type: 'DECREMENT' });

// reducer.js
const initialState = { count: 0 }; 
export const counterReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};

// store.js
import { createStore } from 'redux';
import { counterReducer } from './reducer';
const store = createStore(counterReducer);

// Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './actions';

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

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={() => dispatch(increment())}>Increment</button>
      <button onClick={() => dispatch(decrement())}>Decrement</button>
    </div>
  );
};

export default Counter;

Notes

  • This example demonstrates basic state management with Redux. You can expand it by adding features like reset or asynchronous actions using middleware like Redux Thunk.
  • Ensure you have the Redux and React-Redux packages installed in your project.

Example 2: User Authentication

Context

In many applications, managing user authentication state is crucial. This example illustrates how to manage user login status globally using Redux.

// actions.js
export const login = (user) => ({ type: 'LOGIN', payload: user });
export const logout = () => ({ type: 'LOGOUT' });

// reducer.js
const initialState = { isAuthenticated: false, user: null }; 
export const authReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'LOGIN':
      return { ...state, isAuthenticated: true, user: action.payload };
    case 'LOGOUT':
      return { ...state, isAuthenticated: false, user: null };
    default:
      return state;
  }
};

// store.js
import { createStore } from 'redux';
import { authReducer } from './reducer';
const store = createStore(authReducer);

// Auth.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { login, logout } from './actions';

const Auth = () => {
  const { isAuthenticated, user } = useSelector(state => state);
  const dispatch = useDispatch();

  const handleLogin = () => {
    const user = { name: 'John Doe' };
    dispatch(login(user));
  };

  return (
    <div>
      {isAuthenticated ? (
        <div>
          <h1>Welcome, {user.name}</h1>
          <button onClick={() => dispatch(logout())}>Logout</button>
        </div>
      ) : (
        <button onClick={handleLogin}>Login</button>
      )}
    </div>
  );
};

export default Auth;

Notes

  • This example showcases the management of authentication state, but you can enhance it with API calls for real authentication.
  • Consider using Redux Persist to maintain user session state across page reloads.

Example 3: Shopping Cart

Context

A shopping cart is a common feature in e-commerce applications. This example demonstrates how to manage a shopping cart’s state using Redux, including adding and removing items.

// actions.js
export const addItem = (item) => ({ type: 'ADD_ITEM', payload: item });
export const removeItem = (itemId) => ({ type: 'REMOVE_ITEM', payload: itemId });

// reducer.js
const initialState = { items: [] }; 
export const cartReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      return { ...state, items: [...state.items, action.payload] };
    case 'REMOVE_ITEM':
      return { ...state, items: state.items.filter(item => item.id !== action.payload) };
    default:
      return state;
  }
};

// store.js
import { createStore } from 'redux';
import { cartReducer } from './reducer';
const store = createStore(cartReducer);

// Cart.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addItem, removeItem } from './actions';

const Cart = () => {
  const items = useSelector(state => state.items);
  const dispatch = useDispatch();

  const handleAddItem = () => {
    const newItem = { id: Date.now(), name: 'New Item' };
    dispatch(addItem(newItem));
  };

  return (
    <div>
      <h1>Shopping Cart</h1>
      <button onClick={handleAddItem}>Add Item</button>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.name} <button onClick={() => dispatch(removeItem(item.id))}>Remove</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Cart;

Notes

  • This example provides a base for a shopping cart. You can enhance it by adding item quantity management and total price calculation.
  • Consider implementing selectors for better performance when accessing parts of the state.

Conclusion

These examples demonstrate effective ways to manage global state with Redux in React applications. By leveraging Redux, you can ensure that your application remains organized and maintainable, even as it grows in complexity.