Tutorials / React

React State Management Patterns - Complete Guide

Mahesh Mahesh Waghmare
4 min read Advanced

State management is crucial for React applications. This guide covers all major state management patterns, from local state to global solutions like Redux and Zustand.

Introduction to State Management

State management determines how data flows through your React application. Choosing the right pattern depends on your application’s complexity and requirements.

State Types:

  • Local state - Component-specific
  • Shared state - Multiple components
  • Global state - Application-wide
  • Server state - API data

Management Solutions:

  • useState/useReducer - Local state
  • Context API - Shared state
  • Redux - Complex global state
  • Zustand - Lightweight global state
  • Jotai - Atomic state management

Local Component State

useState Hook

For simple component state:

function Counter() {
    const [count, setCount] = useState(0);
    
    return (
        <div>
            <p>{count}</p>
            <button onClick={() => setCount(count + 1)}>Increment</button>
        </div>
    );
}

useReducer Hook

For complex local state:

function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        default:
            return state;
    }
}

function Counter() {
    const [state, dispatch] = useReducer(reducer, { count: 0 });
    
    return (
        <div>
            <p>{state.count}</p>
            <button onClick={() => dispatch({ type: 'increment' })}>+</button>
            <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        </div>
    );
}
Advertisement

Context API Pattern

Creating Context

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

const ThemeContext = createContext();

export function ThemeProvider({ children }) {
    const [theme, setTheme] = useState('light');
    
    const toggleTheme = () => {
        setTheme(prev => prev === 'light' ? 'dark' : 'light');
    };
    
    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            {children}
        </ThemeContext.Provider>
    );
}

export function useTheme() {
    const context = useContext(ThemeContext);
    if (!context) {
        throw new Error('useTheme must be used within ThemeProvider');
    }
    return context;
}

Using Context

function App() {
    return (
        <ThemeProvider>
            <Header />
            <Content />
        </ThemeProvider>
    );
}

function Header() {
    const { theme, toggleTheme } = useTheme();
    return <button onClick={toggleTheme}>Toggle Theme</button>;
}

Redux Pattern

Redux Setup

npm install @reduxjs/toolkit react-redux

Store Configuration

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

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

Slice Creation

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

const counterSlice = createSlice({
    name: 'counter',
    initialState: { value: 0 },
    reducers: {
        increment: (state) => {
            state.value += 1;
        },
        decrement: (state) => {
            state.value -= 1;
        },
    },
});

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

Using Redux

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

function Counter() {
    const count = useSelector((state) => state.counter.value);
    const dispatch = useDispatch();
    
    return (
        <div>
            <p>{count}</p>
            <button onClick={() => dispatch(increment())}>+</button>
            <button onClick={() => dispatch(decrement())}>-</button>
        </div>
    );
}
Advertisement

Zustand Pattern

Zustand Setup

npm install zustand

Store Creation

import { create } from 'zustand';

const useStore = create((set) => ({
    count: 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
    decrement: () => set((state) => ({ count: state.count - 1 })),
}));

export default useStore;

Using Zustand

import useStore from './store';

function Counter() {
    const { count, increment, decrement } = useStore();
    
    return (
        <div>
            <p>{count}</p>
            <button onClick={increment}>+</button>
            <button onClick={decrement}>-</button>
        </div>
    );
}

Choosing the Right Pattern

When to Use Each:

  • useState: Simple component state
  • useReducer: Complex local state logic
  • Context API: Shared state across component tree
  • Redux: Large applications with complex state
  • Zustand: Lightweight global state needs
  • Jotai: Atomic, granular state management

Conclusion

State management patterns:

  • Start with local state (useState)
  • Use Context for shared state
  • Consider Redux for complex apps
  • Use Zustand for simplicity
  • Choose based on application needs

Understanding these patterns helps you build scalable, maintainable React applications.

Advertisement
Mahesh Waghmare

Written by Mahesh Waghmare

I bridge the gap between WordPress architecture and modern React frontends. Currently building tools for the AI era.

Follow on Twitter