Skip to content

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.

20–30 minEstimated6 questionsTailored live14 model answersTo study
  • Voice interview
  • Live feedback
  • Browser-only
  • No installation required

Your report · sample

AI Readiness Score

/100
Content score English 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.

  1. Step 01

    Preparation

    Skim the topics and warm up — you're briefed before takeoff.

  2. Step 02

    AI Interview

    A realistic AI interviewer asks tailored questions — answer by voice or text.

  3. Step 03

    Instant Score

    Finish and get a 0–100 readiness score across content and English.

  4. Step 04

    AI Feedback

    Concrete tips and a rewritten model answer for every response.

  5. 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.

aevrofy.com/session · live demoPlaying

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?

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?

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?

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?

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 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?

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?

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?

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 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?

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?

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?

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?

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?

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

Aevrofy · React intermediate interview prep