Course

React — The Complete Course

Rendering, components, hooks, state architecture, performance, and React interviewing.

Language course · 6 units

Unit 1: Unit 1 — Rendering, JSX & the reactivity model

React renders from state, not from DOM queries. A re-render happens when state/props change; reconciliation diffs the virtual tree. JSX is sugar for React.createElement. Keys must be stable and unique per list.

Teach: the unidirectional data flow

Parent owns state; children receive it via props and notify via callbacks. This is 'lift state up'. A re-render of the parent re-renders children unless memoized — so performance work is about controlling WHEN components re-render.

Exercises

Practice problems

Unit 2: Unit 2 — Hooks: useState/useEffect/useMemo/useCallback/useRef

Hooks are how you attach state/side-effects to function components. Rules: call at top level, never conditionally, and the order must be stable. useEffect runs after render (cleanup for subscriptions); useMemo caches computed values; useCallback memoizes callbacks; useRef holds a mutable, stable value.

Teach: the dependency array and stale closures

useEffect(fn, deps) re-runs fn only when deps change. Missing a dep captures a stale value (stale closure). useCallback(fn, deps) keeps fn identity stable while deps unchanged so memoized children skip renders. Every value read inside a hook body that is a prop/state belongs in deps.

Exercises

Practice problems

Unit 3: Unit 3 — Performance: memoization, code-splitting, the render budget

Start from 'why does it re-render', not from adding memo everywhere. React.memo skips re-render when props are referentially equal; useCallback/useMemo stabilize props. Code-split with React.lazy + Suspense. Avoid inline object/array props that break memo, measure with Profiler/why-did-you-render.

Teach: referential equality is the whole game

A new {} or [] every render is a new reference, so React.memo can never short-circuit a child that receives it. This is why you useCallback the handler and why you do not define objects inline for memoized children. Correct diagnosis order: what re-renders → why → memo only the hot path.

Exercises

Practice problems

Unit 4: Unit 4 — State architecture: Context, reducers, data fetching, testing

Choose state location deliberately: local state for UI-only, context for cross-cutting theme/auth, reducers when transitions are complex, and server state (react-query/SWR) for fetching/caching. Testing: Testing Library fires user events and asserts on accessible output, not on implementation.

Teach: context is not a state-management silver bullet

Context re-renders every consumer when its value changes — fine for theme/auth (rarely changing), costly for high-frequency data. Split contexts, wrap value in useMemo, and keep server state separate from client UI state. Reach for reducer when state transitions are interdependent.

Exercises

Practice problems

Unit 5: Unit 5 — Components, state & props

Components are functions of (props, state) → UI. Props flow down, state is local, and changes re-render. Master the rendering model: a re-render of a parent re-renders children unless memoized; keys identify list items across re-renders; controlled inputs (value + onChange) vs uncontrolled (ref). Compose via children/props rather than inheritance, and lift state up to the lowest common ancestor that needs it.

Teach: keys and the reconciliation model

React reconciles by element type and key. A stable key (an id, not the array index) lets React reuse the DOM node and preserve local state when a list reorders. Using index as key breaks this when items move — state and focus attach to the wrong row. Controlled inputs: value comes from state and onChange updates it, so the input is always in sync; uncontrolled uses a ref and the DOM owns the value.

Exercises

Practice problems

Unit 6: Unit 6 — Routing, data fetching & the ecosystem

Real apps need routing, server state, and tooling. React Router: route config, nested routes, loaders, navigation, and code-splitting. Server state (react-query/SWR) caches, dedupes, and invalidates fetches — keep it out of local state. Ecosystem: Vite/Next tooling, styling (Tailwind/CSS modules), testing (Vitest + Testing Library), and the mental model of when to reach for a library vs write it.

Teach: route structure and data loading

Define routes declaratively; nested routes render nested layouts. Load data in a route loader (or a query hook) rather than a raw effect, so loading/error/empty states are first-class. Code-split heavy pages with React.lazy + Suspense so the initial bundle stays small. Keep server state in a query library: it handles caching, dedupe, and refetch so you don't hand-roll it.

Exercises

Practice problems

Open this course in Dojo