1. Core Concepts
Global State: When data is needed by many disparate parts of the app (User Profile, Shopping Cart, Theme), Context can be slow or messy. We use libraries.
Redux Toolkit (RTK): The modern industry standard. It wraps Redux to reduce boilerplate.
switch statements in reducers, manual action creators, and immutable update patterns (...state). RTK handles this for you with Immer, but you should know what's happening under the hood.
Zustand: A lighter alternative gaining popularity for less complex apps.
1.1 Deep Dive: Prop Drilling vs. Composition
Before reaching for Redux, ask: "Can I solve this with Composition?"
Instead of passing user down 4 levels, can you pass the <Avatar user={user} /> component itself as a prop? This is often cleaner than global state.
1.2 Industry Standard: Feature Slices
In Redux Toolkit, we organize code by "Feature", not by file type. We don't have a folder for "Actions" and a folder for "Reducers". We have a folder for "Cart" which contains cartSlice.ts (logic) and CartComponent.tsx (UI). This is known as the "Ducks" pattern or "Feature Folder" structure.
1.3 Senior Concept: Server State vs. Client State
The Anti-Pattern: Storing everything in Redux/Zustand.
The Senior Approach: Distinguish between:
1. Client State: UI state (Is the modal open? What is the theme? What is in the cart?). Use Zustand/Redux.
2. Server State: Data from an API (User profile, List of products). This is cache, not state. Use TanStack Query (React Query). It handles caching, deduping, and re-fetching automatically.
📚 Deep Dive (Documentation)
Implement Shopping Cart State
As a customer, I want to add items to my cart and see the total price update instantly, regardless of which page I am on.
- Redux Toolkit Setup:
- Create
cartSlicewithitemsarray. - Actions:
addToCart,removeFromCart,updateQuantity. - Logic: If item exists, increment quantity. Else add new.
- Create
- Selectors:
selectCartTotal: Sum of (price * quantity).selectCartCount: Total number of items.
- UI Integration:
- "Products" page with "Add" buttons.
- "Cart" page displaying items and total.
- Zustand Migration: Re-implement the exact same logic using Zustand in a separate branch/folder.
- Performance: Use selectors in Zustand (
useStore(state => state.items)) to prevent unnecessary re-renders. - Persistence: Use middleware to save the cart to
localStorage.
Complex State Management (Redux 4.x Legacy Support)
As a developer maintaining a legacy enterprise application, I need to understand and work with older Redux patterns while planning a migration to modern standards.
- Legacy Implementation: Implement a feature using "Plain Redux" (v4 style):
- Manual Action Types (constants).
- Switch-case Reducers.
- Immutable update patterns (spread operator hell).
connectHOC (Higher Order Component) for class components.
- Migration Plan: Document a strategy to incrementally migrate this legacy code to Redux Toolkit without breaking the app.
3. The Bug Hunt
🐛 Scenario: The Mutated State
The Setup (Redux):
// In a Redux reducer
state.items.push(newItem); // This works in RTK because of Immer!
// BUT, imagine you are using Context or old Redux:
return { ...state, items: state.items.push(newItem) }; // BUG!
The Bug: Array.push returns the new length of the array, not the array itself. So your state becomes a number (e.g., 3) instead of an object.
The Task:
- Write the correct immutable update logic without Immer.
return { ...state, items: [...state.items, newItem] }.