Microsoft
Course Description
Solve challenging state management problems and build applications that reliably scale with your team. Recognize anti-patterns and learn complex state optimizations with data normalization, form data, and reducers. Refactor messy data stores to leverage an event-driven approach and dive into advanced use cases with URL parameters and syncing with external data stores.
This course and others like it are available as part of our Frontend Masters video subscription.
Preview
CloseCourse Details
Published: July 7, 2025
Rating
Learning Paths
Learn Straight from the Experts Who Shape the Modern Web
Your Path to Senior Developer and Beyond
- 200+ In-depth courses
- 18 Learning Paths
- Industry Leading Experts
- Live Interactive Workshops
Table of Contents
Introduction
Section Duration: 10 minutes
- David Khourshid introduces the course by emphasizing that state management at scale is about maintainability and iteration speed, not just app size. This course is for React developers comfortable with hooks and explores patterns to scale features, manage complexity, and make large codebases easier to understand and debug.
- David provides an overview of the course material, including scalable approaches to managing front-end complexity, key state management concepts, common React pitfalls and anti-patterns, and how to use flexible coding patterns to write clear, maintainable code.
React State Management Anti-Patterns
Section Duration: 20 minutes
- David discusses basic React state anti-patterns, focusing on common pitfalls, such as deriving states using useEffect unnecessarily. He emphasizes recognizing and simplifying these anti-patterns, like calculating derived states directly in render instead of using useEffect, to improve code efficiency and avoid unnecessary renders.
- David explains the importance of using refs in React components for values that should not trigger re-renders, such as a timer ID. He also discusses working with redundant states to maintain a single source of truth and provides an example of selecting a hotel from its ID.
- David instructs students to complete an exercise on recognizing and fixing anti-patterns like redundant states, improper use of refs, and duplicating states. David also suggests referencing static values like user profiles outside of useState to keep functions self-contained and easily testable.
AI State Modeling
Section Duration: 28 minutes
- David discusses incidental and accidental complexity in application development. Incidental complexity is inherent to the problem domain and remains constant regardless of tools used, while accidental complexity arises from implementation choices like frameworks or libraries.
- David discusses three essential diagrams for managing application complexity: entity relationship diagrams, sequence diagrams, and state diagrams. He also recommends tools like dbdiagram.io for entity relationships and swimlanes for sequence diagrams.
- David instructs students to practice brainstorming and creating diagrams to model application states, sequences, and flows. He discusses documenting these aspects to clarify application logic for team collaboration and future development.
Optimizing State Management
Section Duration: 40 minutes
- David covers key principles of effective state management, including using events as the source of truth, pure functions and immutability, framework-independent logic, state machines to avoid invalid states, and declarative side effects.
- David demonstrates how grouping related state into a single object and updating it efficiently can reduce complexity in React. He also introduces finite and type states as alternatives to managing multiple Booleans, helping enforce valid and consistent app states.
- David instructs students to refactor a flight booking form by combining related state variables into a single cohesive state, replacing Boolean flags with finite states, and using string enums or discriminated unions for status handling. He also discusses using arrow functions within handlers and memoization trade-offs.
- David discusses implementing type states to manage different states in a program efficiently by using discriminated unions and intersection types in TypeScript. By combining related data and defining type states, such as idle, submitting, error, and success, the number of variables can be reduced to improve data consistency.
Managing FormData & Complex State
Section Duration: 1 hour, 13 minutes
- David explains simplifying handling forms in React by utilizing the native form data object to eliminate the need for multiple useState hooks for each form field. He demonstrates how to access form data directly and mentions the use of libraries like Zod for form validation.
- David discusses how to simplify form handling using built-in browser and Next.js form tools, emphasizing the use of form data and server-side validation. He demonstrates replacing useState with a status string enum and using the useActionState hook for managing form states efficiently.
- David introduces useReducer as a useful hook for managing complex app logic requirements. He explains how combining UseReducer with React context allows for sharing state and dispatch functions between multiple components, similar to creating a global source of truth like Redux.
- David discusses a pattern in React where, instead of using context, developers can utilize the use hook to pass context directly. He also discusses the performance implications of using context in React and advises on organizing state machines pragmatically based on specific needs before abstracting into generic solutions.
- David instructs students to refactor a complex multi-step form in ExerciseReducer.page.tsx using useReducer to manage state more efficiently. He also walks through a solution, including creating a context, provider, and dispatching actions to streamline state management and improve user experience by centralizing data handling.
- David discusses the limitations of using a linear step-based approach for multi-step processes and proposes a more flexible solution using a directed graph representation. By transitioning from an array of steps to a graph-based structure, the logic simplifies, allowing for branching, looping, and optional steps in the flow.
External State Management Libraries
Section Duration: 29 minutes
- David discusses when to use third-party state libraries like Redux Toolkit, Zustand, Jotai, and XState when built-in patterns no longer scale effectively. He explains two main approaches: store-based solutions for centralized control, and atomic solutions for flexible, reactive updates.
- David demonstrates using XState Store for state management in React components. He also explains using atoms for managing state that can change freely, highlighting how atoms can be used alongside stores to provide fine-grained updates and reduce unnecessary re-renders in components.
- David instructs students to refactor a complex multi-step form with a large reducer and context setup using a third-party state management library like XState Store or Zustand. He also walks through a solution, including creating a store, defining transitions, using selectors to access state, and dispatching events, ultimately streamlining the codebase while maintaining functionality.
Data Normalization
Section Duration: 33 minutes
- David discusses utilizing data normalization to improve performance and code understandability by avoiding deeply nested data structures. He highlights how deeply nested data can lead to performance issues and unnecessary re-renders when updating specific items.
- David instructs students to normalize nested data in a Redux application, focusing on simplifying data structures for easier state management. He also walks through a possible solution of normalizing nested data by moving from nested arrays to a flatter structure, emphasizing the benefits of normalization for efficient state updates and management.
- David demonstrates an implementation of undo and redo functionality using an events-driven architecture in a reducer. By replaying events and keeping track of undos in a stack, users can easily undo and redo actions within an app. Events contain necessary data for actions like undo and redo, making debugging and managing state changes more efficient.
Event-Driven vs Reactive State Management
Section Duration: 12 minutes
- David explains how cascading useEffect hooks in large projects can cause bugs and make data flow harder to trace. He recommends reducing useEffect usage by using a reducer and shifting to a more declarative, event-driven approach to better track and manage state changes.
- David instructs students to refactor code that handles flight and hotel searches triggered by user inputs into a reducer with events to reduce the number of useEffects to a single effect. By centralizing logic based on state changes, the code becomes more organized and declarative, leading to a cleaner and more efficient solution.
Advanced State Management Techniques
Section Duration: 38 minutes
- David highlights the benefits of using URL query parameters for form state, such as preserving data on refresh, enabling sharing, and supporting the back button. He introduces the Nuqs library to simplify syncing state with the URL and emphasizes the value of explicit state management to avoid inconsistent transitions.
- David explains that using useEffect for data fetching can add unnecessary complexity and recommends TanStack Query for a cleaner, more efficient approach. He highlights features like caching, retries, and structured data handling with query clients and keys to better manage loading and mutations.
- David instructs students to refactor a component by replacing multiple useEffect and useState calls with TanStack Query. He demonstrates setting up a query client, using useQuery for fetching, and explains how it simplifies code and handles caching.
- David explains how the useSyncExternalStore hook helps manage external state changes, simplifies code, and prevents issues such as race conditions and hydration mismatches, particularly in frameworks like Next.js.
- David instructs students to refactor code for an exercise by replacing useEffect and useState with useSyncExternalStore to sync flight information. Using subscribe and getSnapshot makes the code cleaner while preserving update functionality.
- David discusses testing reducers and pure logic separately from components. By focusing on pure functions and immutable updates, tests stay accurate and easier to maintain even as the UI changes.
Wrapping Up
Section Duration: 2 minutes
- David wraps up the course by encouraging students to apply the workshop’s lessons across front-end and back-end development. Topics included state patterns, form handling, server actions, data fetching, normalization, and testing.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops