React Context and Advanced State Patterns
September 29th, 2025
Intro
Welcome to Part 4 of the React & Gatsby Learning Series!
In this series, I’m helping Angular developers (and front-end devs in general) understand React and Gatsby step by step. Each post builds on the last, includes a practical project, and highlights key comparisons between Angular and React (and later, Scully and Gatsby).
If you missed the earlier posts, catch up here:
- Part 1 - From Angular to React—Understanding the Mental Model Shift
- Part 2 - React Fundamentals: Components, Props, and Your First App
- Part 3 - Mastering React State with useState and useEffect
The Problem: Prop Drilling
In the last post, we built a Todo app and introduced state management with useState. But what happens when you need to pass data several levels deep?
Example scenario:
Appcomponent → passes user data →Header→NavBar→ProfileLink
This creates prop drilling — passing props through multiple components that don’t actually use them, just to reach a deeply nested child.
- Angular comparison: In Angular, you’d often use a Service with dependency injection to share state across components.
- React solution: Use the Context API to avoid manual prop chains.
The Context API
React’s Context API provides a way to share values (state, functions, themes, etc.) across the component tree without passing props manually.
Steps to use Context:
- Create a context object with
createContext. - Wrap components with a
Provider. - Consume the context using
useContext.
Example – Theme Context:
import { createContext, useContext, useState } from "react";
// 1. Create the context
const ThemeContext = createContext();
// 2. Create a provider component
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme((prev) => (prev === "light" ? "dark" : "light"));
};
return <ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>;
}
// 3. Custom hook for convenience
function useTheme() {
return useContext(ThemeContext);
}
// Example consumer component
function ThemeButton() {
const { theme, toggleTheme } = useTheme();
return <button onClick={toggleTheme}>Current theme: {theme} (click to toggle)</button>;
}
// Usage in App.jsx
function App() {
return (
<ThemeProvider>
<h1>Welcome to the app!</h1>
<ThemeButton />
</ThemeProvider>
);
}
export default App;
-
ThemeProviderwraps the app and supplies values to all descendants. -
useTheme()makes consuming the context easier and cleaner.
When to Use Context
-
Ideal for global state like themes, authentication, or user settings.
-
Avoid using it for everything — large-scale state often benefits from state management libraries like Redux or Zustand.
Think of Context as a lightweight shared state solution.
Beyond Context: useReducer
For complex state updates (like managing objects or multiple transitions), useReducer can be a cleaner alternative to useState.
Example – Todo Reducer:
import { useReducer } from "react";
function todoReducer(state, action) {
switch (action.type) {
case "ADD_TODO":
return [...state, action.payload];
case "REMOVE_TODO":
return state.filter((_, i) => i !== action.index);
default:
return state;
}
}
function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
return (
<div>
<button onClick={() => dispatch({ type: "ADD_TODO", payload: "New Task" })}>Add Task</button>
<ul>
{todos.map((todo, i) => (
<li key={i}>
{todo} <button onClick={() => dispatch({ type: "REMOVE_TODO", index: i })}>Remove</button>
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
-
Reducers follow a predictable state transition pattern.
-
Similar to NgRx in Angular, but built directly into React.
Hands-On Project: Global Theme Switcher
Let’s enhance our Todo app by giving it a global theme toggle with Context.
-
Create a
ThemeContextwithcreateContext. -
Wrap the Todo app with
ThemeProvider. -
Use
useContextto switch styles across the app.
This allows every component (buttons, inputs, lists) to respond to the global theme without passing props down manually.
Closing
✅ That wraps up Part 4: React Context and Advanced State Patterns of the React & Gatsby Learning Series!
Key takeaways from this post:
-
Prop drilling makes data sharing tedious in nested components.
-
React’s Context API provides a simple solution for global state.
-
useReduceris useful for managing complex state logic. -
This mirrors Angular’s Services and dependency injection vs React’s Context.
-
You extended your Todo app with a global theme switcher.
In the next part, we’ll cover React Routing and Navigation, where we’ll build multi-page apps using React Router.
👉 If you found this helpful, follow the series, share with fellow Angular devs, and start experimenting with Context in your own apps!
