You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
As your React applications grow, you need more sophisticated tools for state management and code reuse. This lesson covers useContext, useReducer, compound components, render props, higher-order components, and performance optimisation with React.memo, useMemo, and useCallback.
Prop drilling is passing props through many intermediate components that don't use them:
App → Layout → Sidebar → UserMenu → Avatar
(passes user through every level)
useContext lets you share data across the tree without passing props manually.
import { createContext, useContext, useState, ReactNode } from "react";
// 1. Create the context
interface ThemeContextType {
theme: "light" | "dark";
toggle: () => void;
}
const ThemeContext = createContext<ThemeContextType | null>(null);
// 2. Create a provider component
function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<"light" | "dark">("light");
const toggle = () => setTheme((t) => (t === "light" ? "dark" : "light"));
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}
// 3. Create a custom hook to consume the context
function useTheme() {
const context = useContext(ThemeContext);
if (!context) throw new Error("useTheme must be used within ThemeProvider");
return context;
}
// 4. Use it anywhere in the tree
function ThemeToggle() {
const { theme, toggle } = useTheme();
return (
<button onClick={toggle}>
Current theme: {theme}
</button>
);
}
When state logic is complex or involves multiple sub-values, useReducer is cleaner than multiple useState calls:
import { useReducer } from "react";
interface State {
items: string[];
filter: string;
isLoading: boolean;
}
type Action =
| { type: "SET_ITEMS"; payload: string[] }
| { type: "SET_FILTER"; payload: string }
| { type: "SET_LOADING"; payload: boolean }
| { type: "ADD_ITEM"; payload: string }
| { type: "REMOVE_ITEM"; payload: string };
function reducer(state: State, action: Action): State {
switch (action.type) {
case "SET_ITEMS":
return { ...state, items: action.payload, isLoading: false };
case "SET_FILTER":
return { ...state, filter: action.payload };
case "SET_LOADING":
return { ...state, isLoading: action.payload };
case "ADD_ITEM":
return { ...state, items: [...state.items, action.payload] };
case "REMOVE_ITEM":
return {
...state,
items: state.items.filter((i) => i !== action.payload),
};
default:
return state;
}
}
function TodoApp() {
const [state, dispatch] = useReducer(reducer, {
items: [],
filter: "",
isLoading: false,
});
return (
<div>
<input
value={state.filter}
onChange={(e) =>
dispatch({ type: "SET_FILTER", payload: e.target.value })
}
placeholder="Filter..."
/>
<button
onClick={() => dispatch({ type: "ADD_ITEM", payload: "New item" })}
>
Add Item
</button>
<ul>
{state.items
.filter((item) => item.includes(state.filter))
.map((item, i) => (
<li key={i}>{item}</li>
))}
</ul>
</div>
);
}
useState | useReducer |
|---|---|
| Simple, independent state values | Complex state with multiple sub-values |
| Few state transitions | Many related state transitions |
| State updates are straightforward | State logic is complex or conditional |
Compound components share implicit state and work together as a unit:
import { createContext, useContext, useState, ReactNode } from "react";
// Internal context
interface AccordionContextType {
openIndex: number | null;
toggle: (index: number) => void;
}
const AccordionContext = createContext<AccordionContextType | null>(null);
function useAccordion() {
const ctx = useContext(AccordionContext);
if (!ctx) throw new Error("Must be used within Accordion");
return ctx;
}
// Parent component
function Accordion({ children }: { children: ReactNode }) {
const [openIndex, setOpenIndex] = useState<number | null>(null);
const toggle = (index: number) =>
setOpenIndex((prev) => (prev === index ? null : index));
return (
<AccordionContext.Provider value={{ openIndex, toggle }}>
<div className="accordion">{children}</div>
</AccordionContext.Provider>
);
}
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.