React State Management Patterns - Complete Guide
Mahesh Waghmare 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>
);
}
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>
);
}
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.
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 →