State Management in React: From Context to Redux
Choosing the right state management solution is crucial for building scalable React applications. Let's explore different approaches.
React Context API
Basic Context Setup
import { createContext, useContext, useState } from "react";
const ThemeContext = createContext<{
theme: string;
toggleTheme: () => void;
}>({
theme: "light",
toggleTheme: () => {},
});
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 const useTheme = () => useContext(ThemeContext);
Redux Toolkit
Store Setup
import { configureStore, 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 store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
});
export const { increment, decrement } = counterSlice.actions;
Using Redux in Components
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement } from "./store";
function Counter() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<button onClick={() => dispatch(decrement())}>-</button>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
</div>
);
}
Zustand
Simple State Management
import create from "zustand";
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}));
function Counter() {
const { count, increment, decrement } = useStore();
return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
);
}
Jotai
Atomic State Management
import { atom, useAtom } from "jotai";
const countAtom = atom(0);
function Counter() {
const [count, setCount] = useAtom(countAtom);
return (
<div>
<button onClick={() => setCount((c) => c - 1)}>-</button>
<span>{count}</span>
<button onClick={() => setCount((c) => c + 1)}>+</button>
</div>
);
}
Choosing the Right Solution
When to Use Each
-
Context API
- Simple state sharing
- Theme management
- User preferences
- Small to medium applications
-
Redux
- Complex state management
- Large applications
- Need for middleware
- Time-travel debugging
-
Zustand
- Simple global state
- Minimal boilerplate
- Small to medium applications
- Quick setup
-
Jotai
- Atomic state management
- Fine-grained updates
- React-like API
- Performance optimization
Conclusion
Key considerations for state management:
- Application size and complexity
- Team expertise
- Performance requirements
- Development speed
- Maintenance needs