All interview guides

React Interview Questions and Answers

24 questions that come up in React technical interviews, each with the answer and an explanation of why it is right.

Topics covered: jsx, hooks, state, rendering, props, forms, reconciliation, lifecycle, events, lists, refs, context.

Test yourself — 90 question bank

1. What renders?

Beginner
jsx
function Greeting() {
  const name = 'Sam'
  return <h1>Hello, {name}!</h1>
}

Answer: Hello, Sam!

Curly braces embed a JavaScript expression in JSX, so `{name}` is evaluated rather than printed literally. Anything that produces a value works there — a variable, a function call, a ternary — but statements like `if` do not.

Official documentation →

2. This counter always displays 1 no matter how long it runs. Which line is the cause?

Advanced
jsx
1  function Timer() {
2    const [count, setCount] = useState(0)
3    useEffect(() => {
4      const id = setInterval(() => setCount(count + 1), 1000)
5      return () => clearInterval(id)
6    }, [])
7    return <p>{count}</p>
8  }

Answer: Line 4 — the interval closes over the initial count, which never updates

With an empty dependency array the effect runs once, and the interval callback captures `count` as 0 forever — so it sets 0 + 1 on every tick. The fix is the updater form, `setCount(c => c + 1)`, which reads the latest value instead of the captured one. This is the classic stale closure.

Official documentation →

3. What renders after the button is clicked once?

Intermediate
jsx
function App() {
  const [items, setItems] = useState(['a'])
  const add = () => {
    items.push('b')
    setItems(items)
  }
  return <button onClick={add}>{items.length}</button>
}

Answer: 1 — the screen does not update

The array is mutated in place, so `setItems` receives the reference React already holds. React compares with Object.is, sees no change and skips the re-render. Always create a new array: `setItems([...items, 'b'])`.

Official documentation →

4. This fails to compile. Which line is wrong?

Beginner
jsx
1  function Card() {
2    return (
3      <h2>Title</h2>
4      <p>Body</p>
5    )
6  }

Answer: Lines 3-4 — a component must return a single root element

JSX compiles to a single expression, so sibling elements need a wrapper. Use a `<div>`, or a Fragment (`<>...</>`) when you do not want an extra DOM node — inside a table or a flex container an extra div would break the layout.

Official documentation →

5. This fetches on every single render, hammering the API. Which line is wrong?

Intermediate
jsx
1  function User({ id }) {
2    const [user, setUser] = useState(null)
3    useEffect(() => {
4      fetch(`/api/user/${id}`).then(r => r.json()).then(setUser)
5    })
6    return <p>{user?.name}</p>
7  }

Answer: Line 5 — the dependency array is missing entirely

With no second argument the effect runs after every commit, and because it sets state that schedules another render — an infinite loop. Adding `[id]` runs it only when the id changes. Note that omitting the array is quite different from passing `[]`.

Official documentation →

6. The button is clicked once. What is logged?

Advanced
jsx
function App() {
  const [n, setN] = useState(0)
  const onClick = () => {
    setN(n + 1)
    setN(n + 1)
    console.log(n)
  }
  return <button onClick={onClick}>{n}</button>
}

Answer: 0 is logged, and n becomes 1

State variables are constants within a render, so `n` is 0 throughout the handler and both calls set it to 1. The log also sees 0 — setting state does not change the current render's variable. Using `setN(x => x + 1)` twice would produce 2.

Official documentation →

7. What renders when items is an empty array?

Intermediate
jsx
return <div>{items.length && <List items={items} />}</div>

Answer: A literal 0 on the page

`&&` returns its left operand when falsy, and JSX renders the number 0 as text — unlike false, null and undefined, which render nothing. Use `items.length > 0 && ...` or a ternary. It is one of the most common visual bugs in React.

Official documentation →

8. In React 18, how many times does this component re-render when the button is clicked once?

Advanced
jsx
function App() {
  const [a, setA] = useState(0)
  const [b, setB] = useState(0)
  console.log('render')
  return <button onClick={() => { setA(1); setB(1) }}>go</button>
}

Answer: Once — the updates are batched

React batches state updates that happen in the same event, so both are applied in one re-render. React 18 extended this automatic batching to promises, timeouts and native handlers too — in React 17 an update inside a `setTimeout` would have rendered twice.

Official documentation →

9. What does the child render?

Beginner
jsx
function App() {
  return <Hello name="Ali" age={30} />
}
function Hello({ name, age }) {
  return <p>{name} is {age}</p>
}

Answer: Ali is 30

Props pass data from parent to child. Strings use quotes; anything else — numbers, booleans, arrays, functions — goes in braces. Destructuring in the parameter list is the usual way to read them.

Official documentation →

10. Fill in the blank to add state to this component.

Beginner
jsx
const [count, setCount] = ____(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>

Answer: useState

`useState` returns the current value and a setter. The argument is the initial value, used only on the first render. Changing state through the setter is what tells React to re-render — assigning to `count` directly would do nothing.

Official documentation →

11. The button is clicked once. What is the final value of n?

Intermediate
jsx
const [n, setN] = useState(0)
const onClick = () => {
  setN(n + 1)
  setN(n + 1)
  setN(n + 1)
}

Answer: 1

`n` is a constant within this render, so all three calls compute 0 + 1 and set the same value. The updater form reads the pending value instead — `setN(x => x + 1)` three times would give 3.

Official documentation →

12. This effect fires on every render even though the id rarely changes. Which line is responsible?

Advanced
jsx
1  function Profile({ userId }) {
2    const options = { include: ['posts'] }
3    useEffect(() => {
4      fetchUser(userId, options)
5    }, [userId, options])
6    return null
7  }

Answer: Line 2 — a new object is created each render, so the dependency is never equal

Dependencies are compared with Object.is, and an object literal is a fresh reference on every render, so the check always fails. Either move the constant outside the component, wrap it in `useMemo`, or depend on the primitive values inside it.

Official documentation →

13. What happens when the button is clicked?

Beginner
jsx
function App() {
  let count = 0
  return <button onClick={() => { count++ }}>{count}</button>
}

Answer: The number stays 0 on screen

A plain variable is recreated on every render and changing it does not tell React to re-render, so the screen never updates. State exists precisely for values that should survive renders and trigger one when they change.

Official documentation →

14. Fill in the blank so the input is a controlled component.

Intermediate
jsx
const [name, setName] = useState('')
return <input value={name} ____={e => setName(e.target.value)} />

Answer: onChange

React's `onChange` fires on every keystroke — unlike the native DOM change event, which only fires on blur. Supplying `value` without `onChange` makes the field read-only, and React warns about exactly that in development.

Official documentation →

15. Items are re-ordered. What happens to the input's typed text?

Advanced
jsx
{items.map((item, i) => (
  <li key={i}>
    <input defaultValue={item.name} />
  </li>
))}

Answer: Text stays with the position, so it appears attached to the wrong item

Using the array index as a key tells React that position identifies the element, so when the order changes React reuses the DOM node at each position and only swaps the props. Uncontrolled input state stays put while the labels move. Use a stable id from the data instead.

Official documentation →

16. What is logged when this component mounts?

Intermediate
jsx
function App() {
  console.log('render')
  useEffect(() => console.log('effect'), [])
  return null
}

Answer: 'render' then 'effect'

The component body runs first to produce the element tree; effects run after React has committed to the DOM and the browser has painted. That is why an effect can safely read layout — the DOM already exists by then.

Official documentation →

17. This calls handleClick immediately on render instead of on click. Which line is wrong?

Beginner
jsx
1  function App() {
2    return (
3      <button onClick={handleClick()}>
4        Delete
5      </button>
6    )
7  }

Answer: Line 3 — the function is invoked; pass a reference or a wrapper instead

`handleClick()` calls the function during render and hands its return value to onClick. Pass `handleClick` bare, or wrap it as `() => handleClick(id)` when you need arguments. If the handler sets state, this produces an infinite render loop.

Official documentation →

18. Deleting an item from the middle of this list leaves the wrong checkbox ticked. Which line is the cause?

Intermediate
jsx
1  {todos.map((todo, i) => (
2    <li key={i}>
3      <input type="checkbox" defaultChecked={todo.done} />
4      {todo.text}
5    </li>
6  ))}

Answer: Line 2 — the index as key ties DOM state to position, not to the item

When an item is removed, every later item shifts down an index. React matches by key, so it reuses the DOM node at each position and just swaps the label — the checkbox state stays put. Use a stable `todo.id` as the key.

Official documentation →

19. What renders when isLoggedIn is false?

Beginner
jsx
return <div>{isLoggedIn ? <Dashboard /> : <Login />}</div>

Answer: The Login component

A ternary is the usual way to choose between two elements in JSX, since `if` statements are not expressions and cannot go inside braces. For a single optional element, `{condition && <X />}` is shorter — but be careful when the condition is a number.

Official documentation →

20. What does the child receive?

Intermediate
jsx
function Parent() {
  return <Child count={0} label="" active={false} />
}
function Child({ count, label, active }) {
  return <p>{count} {label || 'none'} {String(active)}</p>
}

Answer: 0 none false

All three props arrive with their real values — 0, an empty string and false are valid props, not missing ones. The `label || 'none'` fallback fires because an empty string is falsy, which is the same trap that `??` avoids.

Official documentation →

21. What does this render?

Beginner
jsx
const items = ['a', 'b']
return <ul>{items.map(i => <li key={i}>{i}</li>)}</ul>

Answer: Two list items: a and b

React renders an array of elements as siblings. Each needs a `key` that is stable and unique among them, so React can match elements across renders — omitting it produces a console warning, and using the array index causes state to stick to positions rather than items.

Official documentation →

22. What does this render after the button is clicked?

Advanced
jsx
function App() {
  const ref = useRef(0)
  const [, force] = useState(0)
  return (
    <button onClick={() => { ref.current++ }}>
      {ref.current}
    </button>
  )
}

Answer: 0 — mutating a ref does not trigger a re-render

A ref is a mutable box that persists across renders but is deliberately outside React's update cycle — changing `.current` schedules nothing. That is exactly why refs suit values you need to remember but not display, such as a timer id or a previous value.

Official documentation →

23. Which of these correctly update a single field in an object held in state? Select all that apply.

Intermediate
jsx
const [form, setForm] = useState({ name: '', email: '' })

Answer: setForm({ ...form, name: 'Sam' })

Spreading into a new object, the updater form, and `Object.assign` onto a fresh target all produce a new reference React will notice. Mutating in place does not — React sees the same object and skips the render. The updater form is safest when several updates may batch together.

Official documentation →

24. Every consumer of this context re-renders on any parent render, even when the value has not changed. Which line explains it?

Advanced
jsx
1  function Provider({ children }) {
2    const [user, setUser] = useState(null)
3    return (
4      <Ctx.Provider value={{ user, setUser }}>
5        {children}
6      </Ctx.Provider>
7    )
8  }

Answer: Line 4 — the object literal is a new reference each render

Context consumers re-render whenever the provider's `value` changes by identity, and a fresh object literal changes every time. Wrap it in `useMemo` keyed on `[user]`. Splitting rarely-changing state into a separate context is the other common remedy.

Official documentation →

Ready to test yourself?

The full React bank has 90 questions across 3 difficulty levels — timed, shuffled, and scored.

Take the React quiz