Module 6: Global State Management

Redux Toolkit (RTK) and Zustand.

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.

Legacy Context (Redux 4.x): Many enterprise codebases still use "plain" Redux (v4). You must understand 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)
REQ-007 To Do

Implement Shopping Cart State

User Story

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.

Acceptance Criteria
  • Redux Toolkit Setup:
    • Create cartSlice with items array.
    • Actions: addToCart, removeFromCart, updateQuantity.
    • Logic: If item exists, increment quantity. Else add new.
  • Selectors:
    • selectCartTotal: Sum of (price * quantity).
    • selectCartCount: Total number of items.
  • UI Integration:
    • "Products" page with "Add" buttons.
    • "Cart" page displaying items and total.
Senior Bonus
  • 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.
REQ-008 To Do

Complex State Management (Redux 4.x Legacy Support)

User Story

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.

Acceptance Criteria
  • Legacy Implementation: Implement a feature using "Plain Redux" (v4 style):
    • Manual Action Types (constants).
    • Switch-case Reducers.
    • Immutable update patterns (spread operator hell).
    • connect HOC (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:

  1. Write the correct immutable update logic without Immer.
  2. return { ...state, items: [...state.items, newItem] }.