Client Application State in React
Learning Outcome Guide
Section titled “Learning Outcome Guide”At the end of this class, you should be able to…
- Differentiate between local and global state in React applications.
- Implement shared state using React’s Context API.
- Identify when to use client-side global state versus server-synchronized data.
Additional Notes:
- Emphasize that Context API solves data-sharing issues but is not ideal for frequently updated or asynchronous data.
- Demonstrate how multiple contexts can be composed for different domains (e.g., AuthContext, ThemeContext).
- React 19 introduces improved context batching and transition handling—mention as future enhancement.
- Prepare students for Lesson 24, where they’ll integrate server-side state management using TanStack Query.
Lesson focus
Section titled “Lesson focus”This lesson introduces shared client state using the React Context API.
We will:
- review how state works in React
- identify when state must be shared across components
- implement a Context provider
- consume shared state in multiple components
- understand when Context is appropriate and when it is not
Connecting to prior lessons
Section titled “Connecting to prior lessons”In recent lessons, we explored:
- how applications render (CSR vs SSR)
- how data can come from the client or the server
This raises an important question:
Where should application state live?
Some state belongs locally inside components. Some state needs to be shared across the application.
Phase 1: Review local component state
Section titled “Phase 1: Review local component state”So far, most state has been local.
Example:
const [count, setCount] = useState(0);Local state works well when:
- only one component needs it
- it does not need to be shared
The limitation
Section titled “The limitation”What happens when multiple components need the same state?
Example scenarios:
- theme (light/dark mode)
- authenticated user
- application preferences
Passing state through many layers leads to prop drilling.
Phase 2: The problem of prop drilling
Section titled “Phase 2: The problem of prop drilling”Prop drilling occurs when state is passed through components that do not use it.
App -> Layout -> Sidebar -> ButtonEven if only the Button needs the value, all intermediate components must pass it along.
This becomes difficult to manage as applications grow.
Phase 3: Introducing Context
Section titled “Phase 3: Introducing Context”The React Context API allows us to:
- share state across the component tree
- avoid passing props through every level
Key idea:
Context provides shared client state to any component that needs it
Phase 4: Create a Context
Section titled “Phase 4: Create a Context”Create a new file:
src/context/ThemeContext.jsAdd:
import { createContext } from 'react';export const ThemeContext = createContext();Phase 5: Create a Provider
Section titled “Phase 5: Create a Provider”Create a new file:
src/context/ThemeProvider.jsximport { useState } from 'react';import { ThemeContext } from './ThemeContext';
export function ThemeProvider({ children }) { const [theme, setTheme] = useState('light');
function toggleTheme() { setTheme((prev) => (prev === 'light' ? 'dark' : 'light')); }
return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> );}Phase 6: Wrap the application
Section titled “Phase 6: Wrap the application”Open main.jsx and wrap your app:
import { ThemeProvider } from './context/ThemeProvider';
createRoot(document.getElementById('root')).render( <StrictMode> <ThemeProvider> <RouterProvider router={router} /> </ThemeProvider> </StrictMode>);Now the entire application has access to the theme context.
Phase 7: Consume context in a component
Section titled “Phase 7: Consume context in a component”In any component:
import { useContext } from 'react';import { ThemeContext } from '../context/ThemeContext';
export default function ThemeToggle() { const { theme, toggleTheme } = useContext(ThemeContext);
return ( <button onClick={toggleTheme}> Current theme: {theme} </button> );}Add a theme toggle button to the application nav bar in Header.jsx:
import { useContext } from 'react';import { ThemeContext } from '../context/ThemeContext';import { NavLink } from 'react-router';
export default function Header({ tagline }) { const { theme, toggleTheme } = useContext(ThemeContext);
return ( <div className="flex flex-wrap items-end justify-between gap-2"> {* existing nav links *} <button className="btn btn-sm cursor-pointer btn-ghost text-xs text-sky-700" onClick={toggleTheme}> Current theme: {theme} </button> </nav> </div> );}Update your index.css to support DaisyUI themes:
@import "tailwindcss";/* @plugin "daisyui"; */@plugin "daisyui" { themes: dark --default, light --prefersdark; }Now, update the PageLayout.jsx file to make use of the context value:
import { useContext } from 'react';import { ThemeContext } from '../../context/ThemeContext';
export default function PageLayout({ header, children }) { const { theme } = useContext(ThemeContext);
return ( <div data-theme={theme} className="min-h-screen bg-base-200"> <header className="border-b border-sky-600 bg-white px-6 py-4"> {header} </header>
<main className="mx-auto grid max-w-7xl grid-cols-1 gap-4 px-6 py-6 md:grid-cols-3"> {children} </main> </div> );}Admittedly, because of the custom CSS overrides that have been applied, the dark styling may not look very good. But, this provides a place to start and demonstrates how to use a context.
Visit the themes page for more information on using theems with daisyUI.
Phase 8: Use context in multiple places
Section titled “Phase 8: Use context in multiple places”You may add the toggle to different parts of your app:
- header
- sidebar
- page component
Observe:
- all components share the same state
- updating in one place updates everywhere
Phase 9: Apply theme to UI
Section titled “Phase 9: Apply theme to UI”You could use the theme value to conditionally style the app.
Example:
<div className={theme === 'dark' ? 'bg-gray-900 text-white' : 'bg-white text-black'}>Phase 10: When to use Context
Section titled “Phase 10: When to use Context”Context is useful for:
- UI preferences (theme, layout)
- authenticated user info
- shared UI state
When NOT to use Context
Section titled “When NOT to use Context”Avoid using Context for:
- frequently changing data
- large complex state trees
- server-fetched data
These cases are better handled with other tools.
Key distinction
Section titled “Key distinction”Client state:
- UI-related
- local or shared in the app
Server state:
- comes from APIs
- changes over time
- requires caching and synchronization
Key Concepts Reinforced
Section titled “Key Concepts Reinforced”- local state is limited to individual components
Contextprovides shared client stateContexthelps avoid prop drillingContextshould not be used for server data
Student Exercise
Section titled “Student Exercise”- Create a context for managing a user preference (e.g., layout, font-size, etc.).
- Provide the context at the application level.
- Consume the context in at least two different components.
- Add a UI control that updates the shared state.
Push to your GitHub workbook repo
Section titled “Push to your GitHub workbook repo”- Stage all changes:
git add -A- Commit:
git commit -m "Lesson 23 - working with context"- Push:
git push origin main