Boarding pass · React Intermediate
React · Intermediate interview
An AI mock interview for React developers with a couple of years' experience. Go past the basics into hooks in depth, context, useReducer, refs, performance and avoiding needless re-renders, custom hooks, and real data-fetching patterns.
- Voice interview
- Live feedback
- Browser-only
- No installation required
Your report · sample
AI Readiness Score
Finish the mock and get this report — scored on content and English, with concrete tips and a model answer for every question, plus a study plan.
Your flight path
Five steps to interview-ready.
From a quick warm-up to a personalised study plan — here's the whole loop, and what you get at each gate.
Step 01
Preparation
Skim the topics and warm up — you're briefed before takeoff.
Step 02
AI Interview
A realistic AI interviewer asks tailored questions — answer by voice or text.
Step 03
Instant Score
Finish and get a 0–100 readiness score across content and English.
Step 04
AI Feedback
Concrete tips and a rewritten model answer for every response.
Step 05
Study Plan
A personalised plan targets your weakest areas — so the next run scores higher.
Preview the AI demo
See one answer scored, end to end.
The whole loop on sample data — question, your answer, instant feedback and a study plan. Hover to pause.
Interviewer · Question 1
“Tell me about yourself.”
Who it's for
- Developers comfortable with React fundamentals
- Engineers who've shipped a few React features
- Anyone targeting mid-level frontend roles
- Self-taught devs filling gaps before interviews
What you'll practice
- Choosing the right hook (useRef, useMemo, useCallback, useReducer)
- Sharing state with Context without prop drilling
- Preventing unnecessary re-renders
- Writing and reusing custom hooks
- Fetching data safely in effects
- Controlled forms with validation
Topics covered
What this level expects.
Hooks in depth
- useRef
- useMemo
- useCallback
- useReducer
- Rules of hooks
Context & state
- Context API
- Avoiding prop drilling
- Lifting state
- useReducer patterns
Effects & data
- Data fetching
- Dependency arrays
- Race conditions
- Refs vs state
Performance
- React.memo
- Re-render causes
- Keys & reconciliation
- Lazy & Suspense
Practice questions
14 questions, with model answers.
Read them, then run the live mock to answer out loud and get scored. Tap any question to reveal a model answer.
01When would you use useRef instead of useState?Hooks in depth
Use useRef for a mutable value that should persist across renders but should NOT trigger a re-render when it changes — like a DOM node reference, a timer/interval id, or the previous value of something. Updating ref.current doesn't re-render; updating state does. So: needs to show on screen → state; just needs to be remembered → ref.
const inputRef = useRef(null); // later: inputRef.current.focus();
02How do useMemo and useCallback differ, and when do they actually help?Hooks in depth
useMemo memoizes a computed value; useCallback memoizes a function (it's essentially useMemo returning a function). They help in two cases: an expensive calculation you don't want to redo every render, or a value/function passed to a memoized child (React.memo) or a hook dependency array, where a fresh reference each render would defeat the optimization. For cheap work they add overhead and clutter — don't reach for them by default.
const sorted = useMemo(() => items.sort(cmp), [items]); const onPick = useCallback((id) => select(id), []);
03What problem does useReducer solve compared to useState?Hooks in depth
useReducer centralizes state transitions in a single reducer function, which is cleaner when you have several pieces of related state, complex update logic, or next-state that depends on the previous state. It makes transitions explicit and testable, and you can pass dispatch down instead of many setters. Reach for it when a cluster of useState calls and their handlers start to feel tangled.
const [state, dispatch] = useReducer(reducer, initial);
dispatch({ type: "increment" });04What are the rules of hooks and why do they exist?Hooks in depth
Call hooks only at the top level of a component or another hook, and never inside loops, conditions, or nested functions; and only call them from React functions. React tracks hook state by call order, so a conditional hook would shift that order between renders and corrupt which state belongs to which hook. The linter plugin enforces both rules.
05How does the Context API help, and what are its downsides?Context & state
Context lets you share a value with a subtree without threading props through every level (prop drilling) — good for things like the current theme, locale, or auth user. The downside is performance: every consumer re-renders when the context value changes, so a frequently-changing value, or a new object literal as the value each render, can cause wide re-renders. Mitigate by splitting contexts, memoizing the value, or pairing it with useReducer.
06Where should state live in a component tree?Context & state
Put state in the lowest common ancestor of the components that need it — 'lift state up' just far enough, no further. Local state stays local; shared state moves up to the nearest parent that owns all consumers; truly app-wide state goes to Context or a store. Keeping state as close to where it's used as possible minimizes re-render scope and keeps components reusable.
07How do you avoid race conditions when fetching data in useEffect?Effects & data
Async responses can arrive out of order, so a stale request can overwrite fresh data. Guard against it with a cancellation flag in the effect's cleanup, or an AbortController. When the dependencies change or the component unmounts, cleanup runs first and you ignore (or abort) the in-flight request so only the latest result is applied.
useEffect(() => {
let active = true;
fetchUser(id).then(u => { if (active) setUser(u); });
return () => { active = false; };
}, [id]);08What goes in the useEffect dependency array, and what happens if you lie to it?Effects & data
List every reactive value the effect reads — props, state, and functions/objects defined in the component that it uses. If you omit one, the effect closes over a stale value and behaves inconsistently; if you add things that change every render, it runs too often. Fix over-running by memoizing dependencies or moving values inside the effect, not by deleting them from the array.
09When should logic NOT be in a useEffect?Effects & data
Effects are for synchronizing with external systems. Don't use them to transform data for rendering (compute it during render, memoize if costly), or to respond to a user event (do that in the event handler). A common smell is an effect that only sets state from props/state — that usually belongs in render or a handler, not an effect.
10What is a custom hook and when would you write one?Hooks in depth
A custom hook is a function whose name starts with 'use' that calls other hooks to package reusable stateful logic — like useDebounce, useLocalStorage, or useFetch. Write one when the same hook logic (an effect + some state) appears in multiple components, or to give a complex piece of logic a clean, named API. It shares logic, not state — each call gets its own isolated state.
function useDebounce(value, ms) {
const [v, setV] = useState(value);
useEffect(() => {
const id = setTimeout(() => setV(value), ms);
return () => clearTimeout(id);
}, [value, ms]);
return v;
}11What causes a component to re-render, and how do you stop unnecessary ones?Performance
A component re-renders when its state changes, its parent re-renders, or a context it consumes changes. Most re-renders are cheap and fine. To cut the wasteful ones: wrap a pure child in React.memo so it skips re-rendering when props are referentially equal, stabilize the props you pass with useMemo/useCallback, and avoid creating new object/array/function literals inline where they feed memoized children.
12What does React.memo do, and when is it pointless?Performance
React.memo wraps a component so it re-renders only when its props change by shallow comparison, skipping renders triggered solely by a parent re-rendering. It's pointless (or harmful) when the component is cheap, when its props change every render anyway (new inline objects/functions), or when it takes children that change each time — the comparison cost then outweighs any saving.
13How do React.lazy and Suspense help, and how do you use them?Performance
React.lazy lets you code-split a component so its bundle is fetched only when first rendered, shrinking the initial load. You render it inside a <Suspense fallback={...}> boundary, which shows the fallback while the chunk (or any suspending data) loads. It's the standard way to defer heavy, rarely-used parts of the UI like modals or routes.
const Chart = React.lazy(() => import("./Chart"));
<Suspense fallback={<Spinner/>}><Chart/></Suspense>14Why are stable, unique keys important for performance and correctness?Performance
Keys let React's reconciler match elements between renders so it can move or reuse DOM nodes instead of tearing them down and rebuilding. Stable unique keys keep component state attached to the right item across reorders and inserts; using the array index as a key breaks this when the list changes, causing wrong state on rows and extra DOM work.
Preparation tips
Walk in ready.
- Be able to explain why a component re-rendered
- Practice writing a custom hook that encapsulates an effect
- Know when memoization helps and when it's just noise
- Implement a debounced search with cleanup
- Refactor prop drilling into Context
- Replace tangled useState with useReducer
Ready for the real thing?
Run a 6-question React mock interview, answer out loud, and get a readiness score with tips and model answers.
More intermediate interviews