Unit 6: Global State, Debugging, and Deployment
I. Orientation — Managing the Application Life Cycle
React applications move through three connected concerns: managing state during execution, diagnosing correctness and performance problems, and packaging the application for production deployment.
- State principle: Keep data as close as possible to the components that use it; broaden its scope only when multiple distant consumers require it.
- Unidirectional data flow: State moves downward through rendered components, while events trigger updates through callbacks, context setters, or dispatched actions.
- State categories:
- Local state: Component-specific values such as an input or modal state.
- Global state: Shared client-side values such as authentication details or theme.
- Server state: Remotely owned data involving fetching, caching, and synchronization.
- Debugging principle: Reproduce and isolate a problem before changing code; use component, network, console, and performance evidence.
- Production principle: A deployable application must be optimized, securely configured, tested, observable, and reproducible from source.
II. State Scope and Solution Selection — Deciding Where Data Belongs
A. State sharing challenges in React applications
Sharing state becomes difficult when data must remain synchronized across components without creating excessive coupling or unnecessary renders.
- Prop drilling: Passing
userthroughApp → Layout → Header → Profileburdens intermediate components that do not use it. - Duplicated state: Storing the same cart total in several components creates conflicting sources of truth; derive it instead:
JSconst total = items.reduce((sum, item) => sum + item.price, 0); - Update ownership: A shared value needs one clear owner and a defined update mechanism, such as a reducer action.
- Render propagation: Updating a high-level object may rerender many descendants, especially when a new object reference is created.
- Persistence difficulty: Refresh-resistant state may require
localStorage, cookies, or server storage rather than ordinary React memory. - Scalability concern: Many unrelated setters and callbacks become harder to trace as application size increases.
B. Comparison of local state, global state, and server state
These state categories differ mainly in ownership, lifetime, consumers, and synchronization requirements.
- Local and global client state:
- Local state:
useStateoruseReducersuits a form field, selected tab, or open dialog used by a nearby component tree. - Global state: Context, Redux, or Zustand suits a theme, authenticated user, or cross-page workflow.
- Local state:
- Server state:
- Remote ownership: Products or messages originate from an API rather than the browser.
- Extra concerns: Server state requires loading, errors, caching, invalidation, refetching, and stale-data handling.
- Typical tools: TanStack Query or RTK Query generally handles remote data better than manually copying every response into global client state.
C. Guidelines for selecting appropriate state management solutions
The simplest solution that preserves clear ownership and predictable updates is usually the most maintainable.
- Use local state: Choose
useStatefor isolated values anduseReducerfor related transitions such as a multi-step form. - Lift state carefully: Move state to the nearest common ancestor when only a small component subtree shares it.
- Use Context: Prefer it for low-frequency, broadly required values such as theme, locale, or authentication metadata.
- Use Redux Toolkit: Select it when complex transitions, middleware, traceability, or many coordinated consumers are required.
- Use Zustand: Select it for a compact external store with minimal ceremony and selective subscriptions.
- Use server-state libraries: Prefer query caching when data must be synchronized with an API.
- Avoid premature globalization: A single input value does not need an application-wide store.
III. Context-Based State — Sharing Values Through the Component Tree
A. Context API for global state management
Context lets a provider expose a value to descendants without passing that value through every intermediate component.
- Core components:
createContextcreates the channel, a provider supplies the value, anduseContextreads it. - Concrete pattern:
JSXconst ThemeContext = createContext(null); function App() { const [theme, setTheme] = useState("light"); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <Dashboard /> </ThemeContext.Provider> ); } const { theme } = useContext(ThemeContext); - Provider boundary: Only descendants of
ThemeContext.Providerreceive its supplied value. - Reference cost:
{ theme, setTheme }creates an object; memoization or separate state/action contexts can limit avoidable updates. - Appropriate use: Context works well for relatively stable cross-cutting data, but it is not automatically a complete state-management architecture.
- Limitation: Frequent provider updates can rerender all consumers that read that context.
IV. Redux Toolkit — Predictable Centralized State
A. Redux Toolkit fundamentals
Redux Toolkit is the recommended Redux approach because it standardizes store setup and reduces manual action and reducer code.
- Store:
configureStorecombines reducers and enables useful development middleware. - Slice:
createSlicegenerates reducer logic and action creators from one definition. - Immutable updates: Immer permits mutation-like syntax such as
state.value += 1while producing immutable state. - Integration:
<Provider store={store}>makes the Redux store available to React components. - Suitable cases: Large shared state, complex transitions, auditability, and middleware commonly justify Redux.
B. Redux data flow and slice architecture
Redux follows a one-way cycle in which actions describe events and reducers calculate the next state.
- Flow: A component dispatches an action; the store invokes reducers; state changes; subscribed components rerender.
- Slice ownership: Feature folders can contain
cartSlice.js, selectors, tests, and related UI. - Example slice:
JSconst counterSlice = createSlice({ name: "counter", initialState: { value: 0 }, reducers: { increment: state => { state.value += 1; } } }); - Action identity: The generated action type is
counter/increment, combining the slice and reducer names. - Reducer rule: Reducers must remain predictable and should not perform network requests, timers, or direct DOM work.
C. Dispatching actions and using selectors
Dispatch sends events to the store, while selectors read specific values from the resulting state.
- Dispatching:
JSXconst dispatch = useDispatch(); <button onClick={() => dispatch(increment())}>Add</button> - Selecting:
JSconst count = useSelector(state => state.counter.value); - Selective rendering:
useSelectorrerenders its component when the selected result changes by reference or value equality. - Parameterized selectors: A selector can locate an entity using an argument, such as
selectTodoById(state, id). - Derived data: Memoized selectors, commonly created with
createSelector, avoid repeatedly computing filtered or aggregated results.
D. Overview of async logic handling
Redux async work occurs outside reducers and usually dispatches lifecycle actions around a promise.
- Thunk mechanism: Middleware allows dispatching a function that receives
dispatchandgetState. - Toolkit helper:
createAsyncThunkautomatically producespending,fulfilled, andrejectedaction types. - State model: A request slice commonly stores
status: "idle" | "loading" | "succeeded" | "failed"pluserror. - API caching: RTK Query adds endpoint definitions, caching, deduplication, invalidation, and generated React hooks.
- Cancellation and races: Request identifiers or abort signals help prevent outdated responses from replacing newer data.
- Reducer boundary: The request occurs in a thunk or query layer; reducers only process its resulting actions.
V. Zustand — Minimal External Stores
A. Lightweight global state management using Zustand
Zustand provides hook-based external stores without providers or Redux-style boilerplate for many common cases.
- Store creation:
JSconst useCartStore = create(set => ({ items: [], add: item => set(state => ({ items: [...state.items, item] })) })); - Selective subscription:
JSconst items = useCartStore(state => state.items);
The component subscribes only to the selected store value. - Advantages: The API is small, actions can live beside state, and stores can be used outside React components.
- Extensions: Middleware can support persistence, immutable updates, and development tooling.
- Trade-off: Its flexibility provides fewer enforced architectural conventions, so large teams must define their own store boundaries and action practices.
VI. Debugging and Performance — Finding Causes with Evidence
A. Debugging React applications
Effective debugging separates symptoms from causes by inspecting state, props, effects, events, and external requests systematically.
- Reproduction: Record the route, user action, browser, input, and exact error before modifying code.
- Console evidence: Use stack traces and targeted logs; remove noisy diagnostic output before release.
- Breakpoints: Browser source breakpoints reveal call stacks and current values without adding permanent logging.
- Network inspection: Verify URL, method, status code, headers, payload, timing, and CORS behavior for API failures.
- Effect errors: Missing dependencies can produce stale values, while unstable object dependencies may trigger repeated effects.
- Error boundaries: Class-based boundaries catch rendering errors in descendants and display fallback UI, but do not catch every event-handler or asynchronous error.
B. React Developer Tools and Profiler usage
React Developer Tools exposes the component tree, while the Profiler measures the cost and cause of commits.
- Components panel: Inspect props, hooks, context values, and component hierarchy.
- Profiler recording: Record a user interaction and examine commit duration, render duration, and rerendered components.
- Flame chart: Wider or highlighted components indicate greater rendering cost in a selected commit.
- Cause inspection: Profiler information can reveal changed props, state, or hooks that caused rendering.
- Production profiling: Measure realistic optimized builds when possible because development checks and Strict Mode can add extra work.
C. Identifying rendering and performance issues
Performance work should target measured bottlenecks rather than applying memoization indiscriminately.
- Common causes: Parent rerenders, unstable object or function props, broad context updates, expensive calculations, and very large lists.
- Stable references:
useMemocaches computed values anduseCallbackstabilizes functions when dependency values remain unchanged. - Component memoization:
React.memocan skip rendering when props are shallowly equal, but comparison also has a cost. - List optimization: Stable keys preserve item identity; virtualization renders only visible rows in a large list.
- Scheduling tools:
useDeferredValueanduseTransitioncan keep urgent interactions responsive during non-urgent rendering. - Primary metric: Use Profiler commit timings and browser performance traces to verify that an optimization actually helps.
VII. Production Build and Configuration — Preparing Reliable Artifacts
A. Build optimization and production readiness techniques
A production build should minimize transferred code, runtime work, and avoidable operational risk.
- Optimized build: Tools such as Vite produce minified, hashed assets and remove unreachable code where tree-shaking applies.
- Code splitting:
JSXconst Reports = lazy(() => import("./Reports.jsx"));
Dynamic imports create separately loaded chunks, often divided by route. - Asset control: Compress images, choose suitable formats, and avoid shipping unused fonts or oversized libraries.
- Bundle analysis: Visualizers identify unexpectedly large dependencies and duplicated modules.
- Readiness checks: Run automated tests, linting, type checking, accessibility checks, and a clean production build.
- Source maps: They aid error diagnosis but should be uploaded or exposed according to the organization’s security policy.
B. Environment configuration using environment variables
Environment variables separate deployment-specific configuration from application source code.
- Vite convention:
JSconst apiUrl = import.meta.env.VITE_API_URL;
Client-exposed Vite variables require theVITE_prefix. - Build-time exposure: Values embedded in a browser bundle are visible to users and must never contain private API keys, database passwords, or signing secrets.
- Environment separation: Development, staging, and production may use different API base URLs and monitoring identifiers.
- Validation: Check required values during startup or build so missing configuration fails clearly.
- Repository safety: Ignore local
.envfiles containing sensitive values and provide a non-secret template such as.env.example.
VIII. Deployment — Delivering and Operating the Application
A. Deployment workflow overview
Deployment transforms a tested source revision into a hosted artifact and verifies that it operates correctly.
- Typical sequence: Commit code, run continuous integration checks, install locked dependencies, build assets, deploy, and perform smoke tests.
- Artifact consistency: A lockfile and fixed runtime version improve reproducibility across local and hosted environments.
- Preview stage: Pull-request deployments allow stakeholders to validate changes before production.
- SPA routing: The host must rewrite unknown application routes such as
/settingstoindex.html. - Rollback: Retain earlier deployments so a defective release can be reversed quickly.
B. Introduction to modern deployment platforms
Modern platforms automate builds, HTTPS, content distribution, preview URLs, and source-control integration.
- Static-focused services: Vercel, Netlify, Cloudflare Pages, and GitHub Pages can host compiled React assets.
- Cloud platforms: AWS, Azure, and Google Cloud provide configurable storage, CDNs, containers, and managed services.
- Git integration: A push or merged pull request can trigger installation, testing, building, and deployment.
- Selection factors: Evaluate routing support, geographic CDN coverage, environment controls, pricing, logging, functions, and rollback facilities.
- Architecture match: A client-only SPA can use static hosting, whereas server rendering requires a compatible Node, edge, or serverless runtime.
C. Best practices for deploying production-ready React applications
Production deployment requires security, observability, resilience, and repeatable release procedures in addition to a successful build.
- Security: Enforce HTTPS, configure suitable security headers, sanitize untrusted content, and keep secrets on trusted servers.
- Caching: Cache hashed assets for long periods while ensuring the HTML entry file can discover new asset versions.
- Monitoring: Collect runtime errors, availability data, and performance measures such as Largest Contentful Paint and Interaction to Next Paint.
- Failure handling: Provide loading, empty, offline, error-boundary, and API-failure interfaces.
- Backend compatibility: Configure CORS, authentication cookies, API URLs, and redirect rules for the production origin.
- Release discipline: Use staging checks, health verification, documented rollback procedures, and small incremental deployments.
- Maintenance: Patch dependencies, audit vulnerable packages, monitor bundle growth, and test supported browsers after significant changes.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →