Skip to content
Intermediate10 min study

React hooks

Model stateful React behavior with stable Hook ordering, render snapshots, refs, custom Hooks, and measured memoization.

Question progress0 / 10 completed
Start the lesson
React hooks visual explanation

WHAT YOU WILL BE ABLE TO DO

Learning outcomes

  • Explain react hooks in plain language.
  • Connect the behavior to the underlying browser or framework model.
  • Implement the core pattern and reason through edge cases.
  • Answer common follow-ups without relying on memorized phrases.

01Overview

Hooks let function components use state, context, and other React features by associating internal data with the position each hook call occupies in a stable, per-render call order — not by attaching data to a class instance.

02Mental model

  • Only call hooks at the top level — never inside conditions, loops, or nested functions.
  • Only call hooks from React function components or other hooks.
  • Conditional hook calls break the call-order guarantee: React matches hook state to call position, so a skipped call shifts every hook after it to the wrong slot.

03Examples

JavaScript
function useToggle(initial = false) {  const [value, setValue] = useState(initial);  const toggle = useCallback(() => setValue((v) => !v), []);  return [value, toggle];}

04Check understanding

Why can't a hook be called inside an if statement?

React identifies each hook's state by its call order across renders; skipping a hook conditionally shifts the association between later hook calls and their stored state, corrupting it.

DDConcept deep dives

Deep dive 1

Each render is a snapshot

Calling a function component produces JSX using the props and state values for that render. Event handlers and effects created during it close over the same snapshot. Setting state requests another render; it does not mutate the value already captured by running code. This model explains batching, stale callbacks, and why logging immediately after a setter shows the previous value.

  • Use a functional updater when the next value depends on queued previous state.
  • Do not mutate state objects; replace the changed path so identity communicates the update.
  • Derive redundant values during render instead of synchronizing duplicate state.

Deep dive 2

Stable call order is React's addressing system

React associates hook state with the order in which Hooks are called by a component. A conditional or loop can shift that order and make the next call read the wrong state slot. Top-level calls preserve the mapping, while conditions belong inside a Hook's callback or around the rendered result.

  • Custom Hooks follow the same rules because they participate in the caller's Hook sequence.
  • A custom Hook shares logic, not state instances.
  • Only React components and custom Hooks may call Hooks.

Deep dive 3

Refs and memoization solve different problems

A ref persists a mutable container without triggering a render; use it for DOM nodes, external handles, timer ids, or data that must be current outside rendering but is not itself visual. useMemo caches a calculated value and useCallback caches a function identity for performance boundaries. Neither should replace state required to describe the UI.

  • Changing ref.current does not schedule rendering.
  • Memoization dependencies follow the same reactive-read principles as effects.
  • Remove memoization if it adds complexity without a measured avoided cost.

QAInterview questions and model answers

Attempt each answer aloud before opening it. The model answer shows the depth and precision expected in an interview; it is not a script to memorize.

Intermediate · Coding · 1 min · Question 1Why must Hooks be called in the same order?Open model answer

Model answer

React associates hook state with call positions in a component's render. Calling hooks conditionally or in loops can shift those positions between renders and attach state to the wrong hook. Hooks therefore run at the top level of components or custom hooks.

JSX
// Breaks the call-order guarantee:if (isLoggedIn) {  const [name, setName] = useState('');}
// Correct: hook is unconditional, the branch is insideconst [name, setName] = useState('');if (isLoggedIn) { /* use name */ }
Open question page →
Intermediate · Coding · 1 min · Question 2What makes a custom Hook different from an ordinary function?Open model answer

Model answer

A custom Hook calls other Hooks and packages reusable stateful behavior. It shares logic, not one state instance: each component call receives independent hook state unless the hook deliberately connects them to a shared external store.

JSX
function useToggle(initial = false) {  const [on, setOn] = useState(initial);  const toggle = useCallback(() => setOn((v) => !v), []);  return [on, toggle];}// Two components calling useToggle() get two independent states.
Open question page →
Intermediate · Coding · 1 min · Question 3When should useMemo and useCallback be used?Open model answer

Model answer

They are performance optimizations for avoiding measured expensive recomputation or stabilizing identity across a meaningful memoization boundary. They add dependency and memory complexity, so do not use them as correctness tools or reflexively wrap every value.

JSX
// Worth it: expensive derivation, or identity a memo child depends onconst sorted = useMemo(() => bigList.slice().sort(compare), [bigList]);const onSelect = useCallback((id) => dispatch(select(id)), []);
Open question page →
Intermediate · Coding · 1 min · Question 4What is a functional state update?Open model answer

Model answer

Passing an updater such as setCount(c => c + 1) asks React to calculate from the queued previous state. It avoids stale snapshots when multiple updates are batched or callbacks intentionally do not depend on a particular render's value.

JSX
setCount(count + 1);setCount(count + 1); // both read the same snapshot -> +1 total
setCount((c) => c + 1);setCount((c) => c + 1); // each builds on the queued value -> +2 total
Open question page →
Intermediate · Coding · 1 min · Question 5What belongs in a ref?Open model answer

Model answer

A ref holds a mutable value that persists across renders without triggering a render when changed. It fits DOM nodes, imperative handles, timer ids, or latest values used outside rendering. Visible UI data belongs in state.

JSX
const intervalId = useRef(null);useEffect(() => {  intervalId.current = setInterval(tick, 1000);  return () => clearInterval(intervalId.current);}, []);
Open question page →
Intermediate · Conceptual · 1 min · Question 6How do you decide whether logic belongs in an event handler or an effect?Open model answer

Model answer

If work happens because the user performed a specific action, keep it in that event flow. If it is required because the component is currently rendered with particular data and must synchronize an external system, use an effect.

Open question page →
Beginner · Conceptual · 1 min · Question 7Why is state called a snapshot?Open model answer

Model answer

The value belongs to one render. Calling a setter schedules another render but cannot alter variables or handlers that already captured the current snapshot.

Open question page →
Advanced · Conceptual · 1 min · Question 8When should state be lifted?Open model answer

Model answer

Move it to the closest common owner when multiple siblings must coordinate one source of truth. Do not lift transient state merely for theoretical reuse.

Open question page →
Advanced · Conceptual · 1 min · Question 9What does useId solve?Open model answer

Model answer

It generates stable ids that coordinate server and client rendering for accessibility relationships. It is not intended as a list key or database identifier.

Open question page →
Advanced · Conceptual · 1 min · Question 10When is useSyncExternalStore appropriate?Open model answer

Model answer

It integrates React with an external mutable store using consistent subscribe, client snapshot, and optional server snapshot contracts, including concurrent rendering behavior.

Open question page →

SCScenario questions

Scenario 1

A custom useWindowSize Hook adds another resize listener for every consuming component.

  1. Define whether the source should be per-consumer or shared.
  2. Use a single external-store subscription when consumers need the same global signal.
  3. Provide a consistent server snapshot for SSR.
  4. Throttle only if measurement shows resize work is costly.
Reveal worked answer

For a global browser source, I would expose it through useSyncExternalStore with shared subscribe and getSnapshot functions. React then coordinates consistent snapshots, and the underlying implementation can reference-count one listener. Each consumer still renders only when the selected value it uses changes.

Verify and go deeper