ReactHooksbeginner
Updated:

React Hooks Interview Questions and Answers

10 min read

The React hooks questions interviewers actually ask — useState, useEffect dependencies, useMemo, custom hooks — with answers you can say out loud and code to back them up.

TL;DR – Quick Answer

React hooks interviews focus on useState and useEffect first, then useRef, useMemo, useCallback, useContext, useReducer and how to write custom hooks. Interviewers care less about definitions and more about the rules of hooks, why the dependency array matters, and how to avoid stale closures and unnecessary re-renders. Expect to explain when a hook runs and to write a small component live.

On This Page

Hooks are the first thing a React interviewer probes, because they reveal in two minutes whether you actually built components or only read about them. This set covers the hooks that come up again and again — useState, useEffect, useRef, useMemo, useCallback and custom hooks — with answers you can speak in a live round and code you can write on a whiteboard.

Why interviewers ask about React hooks

Hooks are how every modern React feature is wired together, so your grasp of them predicts how you will behave in the real codebase. An interviewer wants to know if you understand when a hook runs, not just what it returns.

The trap in most React interviews is not a hard hook — it is a simple one used wrongly. A candidate who cannot explain why a useEffect fires twice, or why a button handler prints an old count, will write bugs that ship. So the questions stay close to daily work: dependency arrays, re-renders, and cleanup.

If you are new to the API, work through the React hooks introduction first, then come back here to rehearse how the same concepts sound as interview answers.

How to answer hook questions

Structure every answer in three beats: what the hook does in one line, when it runs, and one pitfall you have hit. The "when it runs" part is what separates a real developer from someone who memorized the signature.

Wherever you can, write code. React interviews are practical — a five-line snippet that behaves correctly earns more trust than a paragraph. And always tie a hook back to a component you built: "I used useEffect to fetch on mount and cancel the request on unmount" beats a textbook definition every time.

Q1. What is the difference between useState and a normal variable?

A normal variable resets on every render and does not trigger a re-render when you change it. useState gives you a value that React preserves across renders and a setter that schedules a re-render when the value changes.

React re-runs your component function on every render. Any plain let count = 0 is recreated each time, so its updates are lost. useState stores the value outside the function body — React holds it — and returns the current value plus a setter.

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

State updates are also asynchronous and batched: calling setCount does not change count on the current line, it queues a new render.

Interview note: Follow-up: "what does setCount(count + 1) twice in a row do?" It increments only once, because both reads see the same count. Use setCount(c => c + 1) to increment twice.

Q2. When does useEffect run, and what does the dependency array control?

useEffect runs after the render is painted. The dependency array controls how often: [] runs it once after mount, no array runs it after every render, and [a, b] runs it after mount and whenever a or b changes.

The effect is for side effects — data fetching, subscriptions, timers, manual DOM work — things that should not happen during rendering. React compares each dependency to its previous value with Object.is and re-runs only if something changed.

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // runs after mount and whenever count changes

The rule interviewers care about: every value from the component scope used inside the effect must be in the array. Omitting one causes a stale value bug.

Interview note: Trap: "why does my effect run twice in development?" React 18 Strict Mode intentionally mounts, unmounts and remounts components to surface missing cleanup — it does not happen in production.

Q3. How does the useEffect cleanup function work?

The function you return from useEffect is the cleanup. React runs it before the effect runs again and when the component unmounts — so you use it to cancel timers, unsubscribe from events, or abort fetches.

Without cleanup you leak subscriptions and update state on unmounted components. The pattern below adds a listener on mount and removes exactly that listener on unmount.

useEffect(() => {
  const onResize = () => setWidth(window.innerWidth);
  window.addEventListener("resize", onResize);
  return () => window.removeEventListener("resize", onResize);
}, []);

The cleanup closes over the same values as the effect, so listing correct dependencies also keeps cleanup correct.

Interview note: Follow-up: "when exactly does cleanup run with dependencies [userId]?" Before each re-run triggered by a changed userId, and once more on unmount.

Q4. What are the rules of hooks?

Call hooks only at the top level — never inside loops, conditions or nested functions — and call them only from React function components or other custom hooks.

React identifies each hook by the order it is called on every render. If you wrap useState in an if, the order shifts between renders and React attaches state to the wrong hook, corrupting your component.

// Wrong - conditional hook
if (isLoggedIn) {
  const [name, setName] = useState(""); // breaks hook order
}
// Right - hook first, condition inside
const [name, setName] = useState("");
if (isLoggedIn) { /* use name */ }

The ESLint plugin eslint-plugin-react-hooks enforces both rules; mention that you rely on it.

Interview note: Trap: "can you call a hook inside a map?" No — that is a loop; the count of hooks would vary with the array length.

Q5. useMemo vs useCallback — what is the difference?

useMemo caches a computed value; useCallback caches a function. useCallback(fn, deps) is just useMemo(() => fn, deps). Both recompute only when their dependencies change.

You reach for them to avoid expensive recalculation or to keep a stable reference so a memoized child does not re-render. They are optimizations, not correctness tools — adding them everywhere makes code slower and harder to read.

const filtered = useMemo(
  () => items.filter(i => i.active),
  [items]
);
const handleClick = useCallback(() => onSelect(id), [id, onSelect]);

Use useMemo when the computation is genuinely heavy or feeds a dependency array; use useCallback when passing a callback to a React.memo child.

Interview note: Follow-up: "does useMemo guarantee the value is cached?" No — React may discard the cache to free memory, so never rely on it for correctness.

Q6. What is useRef used for?

useRef returns a mutable object whose .current survives re-renders without triggering one. Two main uses: accessing a DOM node, and holding a mutable value like a timer id or the previous state.

Unlike state, changing ref.current does not re-render. That makes it perfect for values you need to remember but do not want to display.

const inputRef = useRef(null);
useEffect(() => inputRef.current.focus(), []);
return <input ref={inputRef} />;

A common non-DOM use is storing an interval id so cleanup can clear it, or tracking whether the component is still mounted.

Interview note: Trap: "why not use a normal variable instead of a ref?" A normal variable resets every render; a ref persists across renders.

Q7. useState vs useReducer — when do you switch?

Use useState for independent, simple values. Switch to useReducer when the next state depends on the previous one, when several values change together, or when update logic gets complex enough to test on its own.

useReducer centralizes transitions in a pure reducer function, which makes complex state predictable and easy to unit test. It shines for forms, wizards and anything with many related fields.

function reducer(state, action) {
  switch (action.type) {
    case "increment": return { count: state.count + 1 };
    case "reset":     return { count: 0 };
    default:          return state;
  }
}
const [state, dispatch] = useReducer(reducer, { count: 0 });

For the bigger picture on choosing between local state, context and reducers, review the React state management overview.

Interview note: Follow-up: "is useReducer better than Redux?" It manages local state; Redux adds a global store, middleware and devtools. They solve different scopes.

Q8. How does useContext work and when should you avoid it?

useContext reads a value from the nearest matching Context.Provider above it, letting you skip prop drilling. Avoid it for values that change very often, because every consumer re-renders when the context value changes.

Context is ideal for stable, app-wide data: theme, current user, locale. For fast-changing state shared widely, a dedicated state library or splitting contexts prevents render storms.

const ThemeContext = createContext("light");
function Button() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Save</button>;
}

Interview note: Trap: "why did all my components re-render?" A new object literal as the provider value creates a fresh reference each render — memoize it with useMemo.

Q9. What is a custom hook and why write one?

A custom hook is a function whose name starts with use and which calls other hooks. You write one to extract and reuse stateful logic — like a data fetch or a form field — across components without duplicating it.

Custom hooks share logic, not state; each component that calls the hook gets its own independent state. This is React's answer to code reuse that class components solved awkwardly with HOCs and render props.

function useToggle(initial = false) {
  const [on, setOn] = useState(initial);
  const toggle = useCallback(() => setOn(o => !o), []);
  return [on, toggle];
}

Being able to design a small custom hook live is one of the strongest signals in an experienced-candidate interview.

Interview note: Follow-up: "do two components using useToggle share state?" No — each call runs its own useState, so state is isolated.

Q10. What is a stale closure and how do you fix it?

A stale closure is when a callback or effect uses an outdated value because it was created on a previous render. The fix is to list the value as a dependency, or use the functional updater form of the setter so you never read the old value.

This bites hardest inside setInterval or event handlers registered once in an effect. The closure captured count at value 0 and never sees updates.

useEffect(() => {
  const id = setInterval(() => setCount(c => c + 1), 1000);
  return () => clearInterval(id);
}, []); // functional update avoids the stale count

Reaching for setCount(c => c + 1) instead of setCount(count + 1) is the cleanest fix and shows you understand closures.

Interview note: Trap: "why did adding count to the deps fix it but restart the interval?" Because the effect re-runs on every count change — the functional updater avoids both problems.

Q11. How do keys work when rendering lists, and why do they matter?

A key is a stable identity React uses to match elements between renders so it can update, move or remove them efficiently. Use a unique, stable id — never the array index when the list can reorder or change length.

Wrong keys cause React to reuse the wrong DOM and component state, producing bugs like text staying in the wrong input row after a delete.

{todos.map(todo => (
  <li key={todo.id}>{todo.text}</li>
))}

Index keys are only safe for a static list that never reorders. This question overlaps heavily with fresher rounds — the React freshers question set drills it further.

Interview note: Follow-up: "what actually breaks with index keys?" On reorder or delete, component state and uncontrolled inputs attach to the wrong item.

Q12. How do you prevent unnecessary re-renders?

Wrap pure child components in React.memo, pass stable callbacks with useCallback, memoize expensive values with useMemo, and keep state as local as possible so fewer components sit in the update path.

The most common cause of wasted renders is passing a fresh function or object to a memoized child every render. Combine React.memo on the child with useCallback/useMemo on the props.

Do not optimize blindly, though — measure with the React Profiler first. Premature memoization adds complexity for no gain. The React performance optimization guide walks through profiling before you touch code.

Interview note: Trap: "does React.memo compare props deeply?" No — it is a shallow comparison, so a new object or array reference defeats it.

How to prepare

Split your prep like this: build one small app (a search box or to-do list) that uses useState, useEffect with cleanup, and one custom hook. Break it on purpose — remove a dependency and watch the stale value appear — because bugs you have seen are answers you cannot forget.

Rehearse the top five hooks out loud until you can explain when each runs without notes. Then pressure-test yourself with the same fresher questions and the broader interview hub for mixed rounds. If you want the full front-end syllabus behind these questions, the Java Full Stack with AI course covers React alongside the backend you will connect it to.

Frequently Asked Questions

Which hooks are asked most often in interviews?
useState and useEffect come up in almost every React interview. After those, expect useRef, useMemo, useCallback, useContext and useReducer, plus at least one question on writing your own custom hook. Freshers get the first two heavily; experienced candidates get performance and custom-hook questions.
What are the rules of hooks and why do they matter?
Only call hooks at the top level of a component or another hook, and never inside loops, conditions or nested functions. React tracks hook state by call order, so calling them conditionally corrupts that order and breaks state. Also only call hooks from React function components or custom hooks, not plain JavaScript functions.
How do I explain the useEffect dependency array in an interview?
The dependency array tells React when to re-run the effect. An empty array runs it once after mount, no array runs it after every render, and listed values run it whenever any of them change. Every value from the component scope used inside the effect must be listed, or you get stale data.
Do I need to know class components for a React interview?
Rarely for new roles, but you should be able to say that hooks replaced lifecycle methods: useEffect covers componentDidMount, componentDidUpdate and componentWillUnmount. Most teams write function components now, so focus your prep there and only skim class lifecycle for legacy-codebase questions.
What is a stale closure and how do I avoid it?
A stale closure happens when an effect or callback captures an old value of state or props because it was created on an earlier render. You fix it by listing the value in the dependency array, or by using the functional updater form of setState so you never read the old value directly.
How much React should a fresher know for interviews?
For a fresher role, be solid on JSX, props, state with useState, side effects with useEffect, lists and keys, controlled form inputs, and conditional rendering. One clean to-do or search component that uses two or three hooks correctly is worth more than memorized API definitions.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 15 July 2026 LinkedIn
Chat with us