State Architecture: Scaling Beyond Components
DraftURL: https://ruvebal.github.io/web-atelier-udit/lessons/en/react/react-state-architecture/
π Table of Contents
- π― Sprint Goal
- π Position in Journey
- π§ Learning Objectives
- ποΈ What Weβll Build This Sprint
- π§ Integration Points
- π Methodology: Atelier Practice
- π Sprint Deliverables
- π Lesson Navigation
- π Key Concepts Preview
βWhen state grows beyond a component, architecture becomes your ally or your enemy.β
π― Sprint Goal
By the end of this sprint: Implement a scalable state architecture that enables features across your app to communicateβwhether thatβs a shopping cart, auth status, or theme settings.
π Position in Journey
| Sprint | Focus | Your App Grows |
|---|---|---|
| 5. Fundamentals | Components, JSX, Props | Component library skeleton |
| 6. Hooks | State & effects | Interactive components |
| β 7. Architecture | Global state | Connected features |
| 8. Routing | Navigation | Multi-page structure |
π§ Learning Objectives
By the end of this lesson, you will:
- Decide when local state isnβt enough
- Implement
useReducerfor complex state logic - Share state via React Context
- Integrate Zustand for lightweight global state
- Understand Redux patterns (conceptually)
- Structure your appβs state by domain
ποΈ What Weβll Build This Sprint
State Domains for Your App
// Organize state by domain:
src/
βββ store/
β βββ auth/ // User session, tokens
β β βββ AuthContext.jsx
β β βββ useAuth.js
β βββ cart/ // Shopping cart (if applicable)
β β βββ useCartStore.js // Zustand
β βββ theme/ // Dark/light mode
β β βββ ThemeContext.jsx
β βββ ui/ // Modals, sidebars, toasts
β βββ useUIStore.js
π§ Integration Points
| Data Source | State Layer |
|---|---|
| Laravel API | Auth tokens in Context, fetched data in React Query |
| Hygraph CMS | CMS content cached by React Query, UI state local |
| Local Storage | Persisted Zustand store (theme, preferences) |
The State Decision Tree
Is this state...
β
βββ Used by 1-2 nearby components?
β βββ useState + prop drilling β
β
βββ Complex with many actions?
β βββ useReducer β
β
βββ Needed by distant components?
β βββ Small/simple? β Context β
β βββ Large/complex? β Zustand/Redux β
β
βββ From a server?
βββ React Query (next sprint) β
π Methodology: Atelier Practice
The Sprint Rhythm
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DAY 1: Context & Reducers β
β β’ Build AuthContext together (login/logout/user) β
β β’ Practice: Refactor a complex useState to useReducer β
β β’ Discussion: When does local state become global? β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β DAY 2: Zustand Workshop β
β β’ Live coding: Create a cart store with Zustand β
β β’ Teams: Implement 1-2 stores for your app's domains β
β β’ AI Practice: Generate actions/selectors β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β DAY 3: Architecture Review β
β β’ Each team presents their state architecture β
β β’ Peer critique: Is this over-engineered? Under? β
β β’ Refactor based on feedback β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
AI-Assisted Development Protocol
| Task | AI Role | Your Role |
|---|---|---|
| Design state shape | Propose structure | Evaluate for your domain |
| Generate reducer actions | Scaffold switch cases | Ensure immutability |
| Debug context re-renders | Explain the problem | Apply memoization |
| Compare libraries | List pros/cons | Decide for your project |
π Sprint Deliverables
- AuthContext with login/logout/user state
- 1 Zustand store for a feature (cart, preferences, etc.)
- useReducer for at least one complex component
- State architecture diagram in your README
- Persistence to localStorage for at least one store
- Reflection: What patterns emerged? What would you change?
π Lesson Navigation
| Previous | Current | Next |
|---|---|---|
| Hooks Mastery | State Architecture | Routing |
π Key Concepts Preview
The State Locality Principle
Keep state as close as possible to where itβs used. Move it βupβ only when:
- multiple siblings need it
- you must persist/share it
- URL needs to represent it
Three tools, three jobs
useReducer: complex local transitions (FSM-like)- Context: dependency injection for shared state
- Store (Zustand/Redux): shared state with selectors + devtools
Example: model state transitions explicitly
// Action shape: { type: 'ADD', id } | { type: 'REMOVE', id } | { type: 'CLEAR' }
Reflection (Atelier)
π Which state did you move too early? What complexity did it add?
π What invariant should your global state always respect?
Koan
βIf your state has no home, it will haunt every component.β
βThe best architecture is the simplest one that solves your problem. No simpler, no more complex.β