State Management Libraries (Redux, Zustand & Recoil)
5 questions foundWhat is Redux and what problem does it solve for larger React apps?
Beginner Redux keeps all of an app's shared state in one single central store, and the only way to change that state is by sending a plain object called an action to a function called a reducer. This makes it very clear and predictable how and why state changes, which becomes valuable in large apps with a lot of shared data.
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
default: return state;
}
}
Real-world example A large e commerce app uses Redux to manage its shopping cart, user session, and notification data all in one predictable, central place, making it easier for a large team to understand exactly how and where state changes happen.
Common follow-ups: Why did Redux become less commonly needed after the introduction of the Context API and hooks?;What is Redux Toolkit and how does it simplify writing Redux code?
State Management;Custom Hooks
How does Zustand offer a simpler alternative to Redux for managing global state?
Intermediate Zustand lets you create a shared store using a simple function, without needing separate actions, reducers, or a Provider wrapping your app. Components read exactly the pieces of state they need directly from a custom hook the store gives you, making the overall setup much shorter and easier to understand.
import { create } from 'zustand';
const useCartStore = create((set) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}));
function Cart() {
const items = useCartStore((state) => state.items);
return <p>You have {items.length} items</p>;
}
Real-world example A team switches their shopping cart state from Redux to Zustand, cutting the amount of setup code significantly while keeping the same reliable shared state behavior across the whole app.
Common follow-ups: Does Zustand require wrapping the app in a Provider component like Redux and Context do?;How does Zustand avoid unnecessary re renders across components?
State Management;Hooks
How does Recoil's atom based approach to state differ from Redux's single central store?
Advanced Recoil breaks shared state into small, independent pieces called atoms, and each component subscribes only to the specific atoms it actually needs, rather than the entire store like Redux. This can reduce unnecessary re renders, since changing one atom does not affect components that only depend on a completely different atom.
import { atom, useRecoilState } from 'recoil';
const cartCountState = atom({ key: 'cartCount', default: 0 });
function CartBadge() {
const [count] = useRecoilState(cartCountState);
return <span>{count}</span>;
}
Real-world example A dashboard app uses separate Recoil atoms for the sidebar's collapsed state and the currently selected report, ensuring that updating one does not cause components relying on the other to re render unnecessarily.
Common follow-ups: How does Recoil's approach compare in terms of learning curve to Zustand's simpler model?;What does an atom's key actually need to be unique across?
State Management;Performance (memo/useMemo)
How do middleware, such as Redux Thunk, help handle asynchronous operations like API calls in Redux?
Intermediate Regular Redux actions are plain objects and cannot directly contain asynchronous logic like a fetch call. Middleware like Redux Thunk lets you dispatch a function instead of a plain object, and that function can perform asynchronous work, like fetching data, before eventually dispatching the real action with the actual result.
function fetchUser(id) {
return async function (dispatch) {
dispatch({ type: 'FETCH_USER_START' });
const user = await fetch(`/api/users/${id}`).then((r) => r.json());
dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
};
}
store.dispatch(fetchUser(5));
Real-world example A team fetches user profile data through a thunk action, which dispatches a loading action first, then dispatches a success action once the actual data arrives from the server, keeping their Redux store correctly updated throughout the process.
Common follow-ups: How does Redux Toolkit's built in createAsyncThunk simplify writing this same pattern?;What is the difference between Redux Thunk and Redux Saga for handling asynchronous logic?
Data Fetching (React Query & SWR);Custom Hooks
How do you decide which state management library, if any, is right for a specific React project?
Beginner For most small to medium apps, useState and Context together are often perfectly sufficient. Zustand is a good lightweight choice when a bit more shared global state organization is needed without much setup. Redux fits very large apps with complex state logic and a need for strict predictability, often across a large team. The right choice depends on the app's actual size and complexity, not just personal preference.
// Small app, Context and useState are enough
const ThemeContext = React.createContext('light');
// Larger app, needing more structure
const useAppStore = create((set) => ({ /* shared state and actions */ }));
Real-world example A small personal project sticks with plain useState and Context, while a growing startup's product with dozens of engineers adopts Redux Toolkit specifically for its strict, predictable structure across a large codebase.
Common follow-ups: Is it common for a single app to use more than one of these approaches together?;How easy is it to migrate from one state management approach to another later?
State Management;Design Patterns in React