Skip to content
RED SAGSkills

React interview questions and answers

React interview questions focus on how rendering really works: when components re-render, how state and effects behave, and how to keep a UI fast. Many questions show a short component and ask what goes wrong.

Below are 60 React questions with the correct answer and a short explanation, grouped from fresher to experienced level. Try each one before you open the answer.

What React interviews usually check

  • ✓JSX, components, props and state
  • ✓useState, useEffect, useMemo, useCallback and custom hooks
  • ✓Re-renders, keys, reconciliation and memoisation
  • ✓Forms, controlled inputs and lifting state up
  • ✓Context, data fetching and common performance traps

React interview questions for freshers easy

1. Why React lists need keys

Why does React ask you to give each item in a rendered list a stable key prop?

  1. A.So React can match items between renders and keep the right state and DOM nodes when the list changes
  2. B.Keys are required for CSS selectors to target list items
  3. C.Keys make the list render in sorted order
  4. D.Keys are sent to the server for SEO
Show answer

Answer: A. So React can match items between renders and keep the right state and DOM nodes when the list changes

During reconciliation React uses keys to know which item is which. With stable keys, React can move, add or remove items while keeping each component state attached to the right item. Using the array index as a key can cause wrong state when items are reordered or inserted.

2. Input that ignores typing

Users cannot type in this input: the text never changes. Why?

function NameField() {
  const [name, setName] = useState('')
  return <input value={name} />
}
  1. A.useState cannot hold strings, only numbers and objects
  2. B.Inputs in React must always use the defaultValue prop
  3. C.The input is controlled by state, but nothing updates the state when the user types
  4. D.The component must be a class component to handle input
Show answer

Answer: C. The input is controlled by state, but nothing updates the state when the user types

Passing value makes the input controlled: React always shows the state value, which stays empty. You need an onChange handler like onChange={(e) => setName(e.target.value)}. If you do not need React to own the value, use defaultValue for an uncontrolled input instead.

3. Returning two elements

A component must return an <h2> and a <p> side by side with no extra wrapper div in the DOM. What should it return?

  1. A.Two return statements
  2. B.A string joining both tags
  3. C.<><h2 /><p /></>, a Fragment
  4. D.An object { h2, p }
Show answer

Answer: C. <><h2 /><p /></>, a Fragment

A component returns one value, so siblings need a parent. A Fragment (<>...</> or <Fragment>) groups them without adding a DOM node, which keeps CSS layouts like flex and table rows intact.

4. Content between component tags

How does a Card component show whatever is placed between <Card> and </Card>?

  1. A.It reads this.innerHTML
  2. B.It calls React.getContent()
  3. C.It renders props.content
  4. D.It renders props.children
Show answer

Answer: D. It renders props.children

Anything nested between a component’s tags is passed as the children prop. Rendering {children} inside the component places it there, which is how layout and wrapper components are built.

5. Hiding a component

A Banner component should render nothing when the user has dismissed it. What should it return?

  1. A.null
  2. B.An empty <div> element
  3. C.false wrapped in a Fragment
  4. D.The string "none"
Show answer

Answer: A. null

Returning null from a component renders nothing. Since React 18, returning undefined is also allowed, but null makes the intent clear. The component still runs, so its hooks keep working.

6. Logs that appear twice

In development, a console.log in a component body appears twice per render, and effects run, clean up and run again on mount. Why?

  1. A.React has a known bug in its development builds
  2. B.The component is rendered by two parents
  3. C.console.log is called once for each hook
  4. D.StrictMode does it in development to expose bugs
Show answer

Answer: D. StrictMode does it in development to expose bugs

StrictMode renders components twice and re-runs effects once on mount, in development only, to surface side effects in render and missing cleanups. Production renders once. Code that is pure and cleans up correctly is not affected.

7. When effect cleanup runs

An effect subscribes to a chat room for roomId and returns a cleanup that unsubscribes. When does cleanup run?

  1. A.Before the effect re-runs, and on unmount
  2. B.Only when the browser tab closes
  3. C.Right after the effect runs each time
  4. D.Never, unless you call it yourself
Show answer

Answer: A. Before the effect re-runs, and on unmount

When roomId changes, React runs the old cleanup (leaving the old room) before running the effect with the new value. It also runs cleanup when the component unmounts, so subscriptions never pile up.

8. Focusing an input on a button click

Which hook gives you access to an input’s DOM node so you can call .focus() from a handler?

  1. A.useState
  2. B.useMemo
  3. C.useRef
  4. D.useContext
Show answer

Answer: C. useRef

Create const inputRef = useRef(null), attach it with <input ref={inputRef} />, and call inputRef.current.focus() in the handler. React sets current to the DOM node after it is mounted.

9. Reading the current language

A LanguageContext Provider wraps the app with value "ar". A deep component calls useContext(LanguageContext). What does it get?

  1. A.The default value passed to createContext
  2. B.undefined until a prop is passed
  3. C."ar", from the nearest Provider above it
  4. D.An object with value and setValue
Show answer

Answer: C. "ar", from the nearest Provider above it

useContext returns the value of the closest matching Provider above the component in the tree. When that Provider’s value changes, every component reading the context re-renders with the new value.

10. What useCallback returns

What does useCallback(fn, [id]) give you?

  1. A.The result of calling fn
  2. B.A function that runs only once ever
  3. C.A debounced version of fn
  4. D.The same function object until id changes
Show answer

Answer: D. The same function object until id changes

useCallback caches the function itself, not its result. The reference stays the same across renders until a dependency changes, which helps when passing callbacks to memoised children or effect dependencies.

11. Splitting off a heavy admin page

Which code loads the AdminPanel component only when it is first rendered?

  1. A.import AdminPanel from "./AdminPanel" at the top
  2. B.lazy(() => import("./AdminPanel")) plus a Suspense
  3. C.const AdminPanel = require("./AdminPanel") in render
  4. D.useEffect(() => { import("./AdminPanel") }, [])
Show answer

Answer: B. lazy(() => import("./AdminPanel")) plus a Suspense

React.lazy takes a function that returns a dynamic import, and the bundler puts AdminPanel in its own chunk. While it loads, the nearest Suspense boundary shows its fallback.

12. Finding a button in a test

React Testing Library recommends which query to find the "Place order" button?

  1. A.container.querySelector(".btn-primary")
  2. B.screen.getByTestId("place-order") always
  3. C.wrapper.find("PlaceOrderButton")
  4. D.getByRole("button", { name: "Place order" })
Show answer

Answer: D. getByRole("button", { name: "Place order" })

Role queries find elements the way users and assistive technology do, by role and accessible name. They also catch accessibility bugs, such as a div used as a button. Test ids are a last resort.

13. Asserting an error is gone

In React Testing Library, how do you assert that the text "Invalid OTP" is NOT on screen?

  1. A.expect(screen.queryByText("Invalid OTP")).toBeNull()
  2. B.expect(screen.getByText("Invalid OTP")).toBeNull()
  3. C.expect(screen.findByText("Invalid OTP")).toBeNull()
  4. D.expect(screen.getByText("Invalid OTP")).toBeFalsy()
Show answer

Answer: A. expect(screen.queryByText("Invalid OTP")).toBeNull()

getBy queries throw when nothing matches, so they cannot express absence. queryBy returns null instead of throwing, which makes it the right tool for "not there" checks. findBy returns a promise.

14. Starting a new React project

Create React App was deprecated in 2025. Which is a recommended way to start a new client-side React app today?

  1. A.npx create-react-app my-app
  2. B.npm install react and open index.html
  3. C.npm create vite@latest
  4. D.npx react-scripts start
Show answer

Answer: C. npm create vite@latest

The React team deprecated Create React App and points to frameworks such as Next.js or React Router, or a build tool like Vite for a plain client-side app. Vite starts fast and has a React template.

15. Why use a data-fetching library

What does a library like TanStack Query give you over a hand-written useEffect + fetch?

  1. A.Faster network speed for every request
  2. B.Automatic database access from the browser
  3. C.Caching, deduping, retries and background refresh
  4. D.Server-side rendering with no server
Show answer

Answer: C. Caching, deduping, retries and background refresh

Query libraries cache server data by key, share it between components, remove duplicate requests, retry failures and refetch stale data. Doing all that correctly with useEffect by hand is a lot of code.

16. A secret key in import.meta.env

A Vite app reads import.meta.env.VITE_PAYMENT_SECRET to sign requests in the browser. What is the problem?

  1. A.VITE_ variables are put into the public JS bundle
  2. B.Vite does not support environment variables
  3. C.The variable is only available in tests
  4. D.import.meta only works in Node.js
Show answer

Answer: A. VITE_ variables are put into the public JS bundle

Vite inlines every VITE_-prefixed variable into the client code at build time, so anyone can read it in the browser. Secrets must stay on a server, and the browser should call that server instead.

17. Choosing the selected option

How do you make a React <select> show "AE" as the chosen country from state?

  1. A.<select value={country} onChange={...}>
  2. B.Add selected to the matching <option>
  3. C.<select default="AE">
  4. D.<select selectedIndex={country}>
Show answer

Answer: A. <select value={country} onChange={...}>

React controls <select> through a value prop on the select itself, plus onChange to update state. Using selected on an option is the plain HTML way and triggers a warning in React. defaultValue is for uncontrolled selects.

18. A name containing a script tag

A user sets their name to <script>alert(1)</script>. What happens when you render <p>{user.name}</p>?

  1. A.The script runs in the browser
  2. B.React throws an error and stops
  3. C.The text shows as is; React escapes it
  4. D.The name is shown as an empty string
Show answer

Answer: C. The text shows as is; React escapes it

React escapes values placed in JSX, so markup inside strings is shown as text, not parsed as HTML. The risk returns if you use dangerouslySetInnerHTML or put user input into href or src without checks.

19. Linking a label in JSX

Which JSX correctly links a label to an input with id "email"?

  1. A.<label htmlFor="email">Email</label>
  2. B.<label for="email">Email</label>
  3. C.<label forId="email">Email</label>
  4. D.<label input="email">Email</label>
Show answer

Answer: A. <label htmlFor="email">Email</label>

for is a reserved word in JavaScript, so JSX uses htmlFor, matching the DOM property. Clicking the label then focuses the input, and screen readers announce the label with it.

20. Calling setCount twice

In this React Native component, count starts at 0. What is count after the user presses the button once?

const [count, setCount] = useState(0)

const onPress = () => {
  setCount(count + 1)
  setCount(count + 1)
}
  1. A.0
  2. B.2
  3. C.It throws, because state was set twice
  4. D.1
Show answer

Answer: D. 1

Both calls use the same count value captured in this render, which is 0, so both set the state to 1. React batches them into one re-render with count = 1. To add twice, use the updater form setCount(c => c + 1), which works on the latest value.

React intermediate interview questions medium

21. Resize handler that runs many times

After opening and closing this panel a few times, every window resize runs the handler several times, even for panels that are already closed. What is the fix?

useEffect(() => {
  window.addEventListener('resize', () => setWidth(window.innerWidth))
}, [])
  1. A.Add width to the dependency array
  2. B.Use a passive event listener instead
  3. C.Store the handler in a variable and return a cleanup that calls removeEventListener with it
  4. D.Move the listener into the render body so it runs on every render
Show answer

Answer: C. Store the handler in a variable and return a cleanup that calls removeEventListener with it

Each mount adds a new listener, and nothing removes it when the component unmounts. The effect should return a cleanup function that removes the same function reference it added, which is why an inline anonymous handler cannot be removed. Strict Mode in development mounts twice on purpose to expose missing cleanups like this.

22. Passing a prop through five layers

A user object is passed through Layout, Sidebar and Menu only so Avatar deep inside can use it. Which approach often removes the drilling without context?

  1. A.Copy user into every layer’s own useState
  2. B.Pass <Avatar user={user} /> down as children
  3. C.Store user on window and read it in Avatar
  4. D.Use a useRef in each layer to hold user
Show answer

Answer: B. Pass <Avatar user={user} /> down as children

Component composition lets the parent create the deep element itself and pass it down as children or a slot prop. The middle layers just place it and no longer need to know about user. Context is useful when many distant components need the value.

23. Two components using one custom hook

Header and Footer both call useCart(), a custom hook that has useState inside. Adding an item in Header does not update Footer. Why?

  1. A.Each call to a custom hook gets its own separate state
  2. B.Custom hooks can only be used in one component
  3. C.useCart must be wrapped in memo
  4. D.State in custom hooks is reset on every render
Show answer

Answer: A. Each call to a custom hook gets its own separate state

Custom hooks reuse stateful logic, not state. Every component that calls useCart gets its own copy of the state. To share one cart, lift the state up or put it in context or an external store.

24. Unique ids for a reusable field

A reusable TextField renders a <label htmlFor> and <input id>. It is used many times on a page and with server rendering. How should it make the id?

  1. A.const id = Math.random().toString()
  2. B.const id = "field"
  3. C.let counter++ in a module variable
  4. D.const id = useId()
Show answer

Answer: D. const id = useId()

useId returns an id that is unique per component instance and the same on server and client, so hydration matches. Math.random gives different values on server and client, and a fixed id clashes when the field is used twice.

25. Default prop values

In modern React function components, how should a Button default size to "md" when no size prop is passed?

  1. A.Button.defaultProps = { size: "md" }
  2. B.if (!props.size) props.size = "md"
  3. C.useState("md") inside Button
  4. D.function Button({ size = "md" }) { ... }
Show answer

Answer: D. function Button({ size = "md" }) { ... }

Default values in destructuring are the standard approach. defaultProps for function components was removed in React 19, and props are read-only, so assigning to props is wrong.

26. useMemo that recomputes every time

Why does this useMemo recalculate on every render?

const filters = { city, minPrice }
const results = useMemo(() => search(items, filters), [items, filters])
  1. A.useMemo never caches arrays
  2. B.filters is a new object each render
  3. C.items must be listed last
  4. D.search is not a pure function
Show answer

Answer: B. filters is a new object each render

Dependencies are compared with Object.is. filters is created fresh on each render, so it is never equal to the previous one. Depend on city and minPrice directly, or create filters inside the memo function.

27. useCallback that changes nothing

A developer wraps every handler in useCallback, but the child components are plain functions without memo. What is the effect on re-renders?

  1. A.Children stop re-rendering completely
  2. B.The page re-renders twice as often
  3. C.Handlers stop working after the first click
  4. D.None; children re-render anyway
Show answer

Answer: D. None; children re-render anyway

A stable function reference only helps if something compares it, such as a memo child or an effect dependency. A non-memo child re-renders whenever its parent does, so useCallback just adds overhead here.

28. A slow page from one hover state

A product page keeps isHovered state at the top level to style one button, and the whole page re-renders on hover. What is the cleanest fix?

  1. A.Move the state into a small button component
  2. B.Wrap the whole page component in useMemo
  3. C.Store isHovered in a global module variable
  4. D.Replace the state with a useEffect that sets it
Show answer

Answer: A. Move the state into a small button component

State causes re-renders of the component that owns it and its children. Moving it into a small component that only wraps the button limits re-renders to that part. Often a CSS :hover rule removes the need for state entirely.

29. Cleaning up a window listener in an effect

Which effect correctly adds and removes a keydown listener?

  1. A.add (e) => onKey(e); return () => remove (e) => onKey(e)
  2. B.add onKey in the effect; remove it in a separate effect
  3. C.const h = (e) => onKey(e); add h; return () => remove h
  4. D.add onKey; no cleanup, since React removes it
Show answer

Answer: C. const h = (e) => onKey(e); add h; return () => remove h

removeEventListener needs the same function reference that was added. Defining the handler once inside the effect and removing that same variable in the cleanup works. React does not know about listeners you add with addEventListener.

30. A Server Component inside a client wrapper

A "use client" Tabs component needs to show a server-rendered ProductDetails inside it. What is the right way?

  1. A.Import ProductDetails inside the Tabs file
  2. B.Pass <ProductDetails /> into Tabs as children
  3. C.Add "use client" to ProductDetails as well
  4. D.Fetch ProductDetails HTML with useEffect
Show answer

Answer: B. Pass <ProductDetails /> into Tabs as children

Anything imported into a client file becomes client code. But a Server Component parent can render <Tabs><ProductDetails /></Tabs>; the server output is passed as children, so ProductDetails stays on the server.

31. userEvent versus fireEvent

Why is await user.type(input, "9876") from @testing-library/user-event usually preferred over fireEvent.change?

  1. A.It runs tests without needing any DOM
  2. B.It is synchronous, so no await is needed
  3. C.It simulates real key presses and focus
  4. D.It skips onChange handlers to run faster
Show answer

Answer: C. It simulates real key presses and focus

user-event fires the full series of events a real user causes: focus, keydown, keypress, input and keyup for each character. fireEvent dispatches one event, which can miss bugs in handlers that depend on the others.

32. Testing text that appears after a fetch

A test fails because getByText("Welcome, Ravi") runs before the mocked API responds. What is the fix?

  1. A.screen.getByText("Welcome, Ravi", { wait: true })
  2. B.setTimeout(() => screen.getByText(...), 1000)
  3. C.await screen.findByText("Welcome, Ravi")
  4. D.screen.queryByText("Welcome, Ravi")
Show answer

Answer: C. await screen.findByText("Welcome, Ravi")

findBy queries return a promise that retries until the element appears or a timeout (1 s by default) passes. Fixed timeouts make tests slow and flaky, and queryBy returns null immediately.

33. The memo compare function

memo(Row, (prev, next) => prev.id === next.id && prev.price === next.price). What does returning true mean?

  1. A.The props are equal, so Row skips re-rendering
  2. B.Row must re-render now
  3. C.React throws away the old props and result
  4. D.React compares the props again, deeply this time
Show answer

Answer: A. The props are equal, so Row skips re-rendering

The second argument to memo is arePropsEqual. Returning true says the props are equivalent and the previous result can be reused. Ignoring props such as callbacks this way can cause stale closures, so use it carefully.

34. Sharing logic: hook or HOC

Several components need the current online/offline status. Why is a custom hook usually preferred over a higher-order component today?

  1. A.HOCs are no longer allowed at all in React 19
  2. B.Hooks run on a separate thread from rendering
  3. C.Hooks cache the data between different users
  4. D.It shares logic without wrappers or prop clashes
Show answer

Answer: D. It shares logic without wrappers or prop clashes

A hook like useOnlineStatus() returns a value the component uses directly. HOCs wrap components, add layers to the tree, can hide where props come from and may collide on prop names. HOCs still work but are rarely needed now.

35. Where focus goes after a modal

A custom React modal closes, and keyboard focus jumps to the top of the page. What should happen instead?

  1. A.Focus returns to the button that opened the modal
  2. B.Focus moves to the browser address bar
  3. C.Focus moves to the footer
  4. D.Focus is removed from the page
Show answer

Answer: A. Focus returns to the button that opened the modal

Accessible dialogs move focus inside when opened, keep it there while open and return it to the triggering element when closed, so users continue where they were. The native <dialog> element with showModal() does much of this for you.

36. A list that stops updating

A component copies TanStack Query data into useState in an effect, then edits the copy. After a background refetch, the list shows old data. Why?

  1. A.TanStack Query never refetches
  2. B.The copy is a second source of truth that drifts
  3. C.useState cannot hold arrays that came from a server
  4. D.Effects never run after refetch
Show answer

Answer: B. The copy is a second source of truth that drifts

Server data already lives in the query cache. Copying it into local state creates a second copy that drifts. Render from the query data directly, keep only the user’s edits in local state, and save changes with a mutation.

37. User bios rendered as HTML

User bios are written in Markdown, converted to HTML on the server, and shown with dangerouslySetInnerHTML. What is the right way to make this safe?

  1. A.Nothing is needed; React escapes HTML automatically
  2. B.Sanitize the HTML with an allowlist sanitizer like DOMPurify, plus a CSP
  3. C.Strip every <script> tag from the bio with a regular expression before saving it
  4. D.Only allow bios shorter than 200 characters
Show answer

Answer: B. Sanitize the HTML with an allowlist sanitizer like DOMPurify, plus a CSP

dangerouslySetInnerHTML turns off React’s escaping, so any HTML in the Markdown output runs in other users’ browsers as stored XSS. An allowlist sanitizer removes dangerous tags and attributes like onerror or javascript: links. Blocklisting words is easy to bypass, and CSP adds a second layer of defense.

38. ref.watch versus ref.read in Riverpod

In a Riverpod ConsumerWidget, a developer uses `ref.read(userProvider)` inside build to show the user name. What problem follows?

  1. A.The app crashes because read is not allowed in build
  2. B.The provider is disposed after every frame
  3. C.The widget will not rebuild when userProvider changes
  4. D.The widget rebuilds on every frame, wasting CPU
Show answer

Answer: C. The widget will not rebuild when userProvider changes

ref.watch subscribes the widget and rebuilds it when the provider value changes. ref.read only takes the current value, so the name stays stale. Use watch for values shown in build and read for one-off actions such as button handlers.

39. Effect runs on every render

Why does this effect run after every render?

const filters = { category, sort };
useEffect(() => {
  fetchProducts(filters).then(setProducts);
}, [filters]);
  1. A.useEffect ignores dependency arrays in React Native
  2. B.fetchProducts is async, so effects always rerun
  3. C.filters is a new object every render, so it always differs
  4. D.The array must list setProducts as well
Show answer

Answer: C. filters is a new object every render, so it always differs

React compares dependencies with Object.is. An object literal created during render is a new reference each time, so the effect sees a change and runs again. Depend on the primitive values (category, sort) or memoise the object with useMemo.

40. Keeping React input responsive

In a React app, a tab click triggers a large, slow re-render. How can startTransition help INP?

  1. A.It marks the update as non-urgent so urgent updates paint first
  2. B.It moves the rendering work into a web worker automatically for you
  3. C.It skips rendering the new tab content entirely
  4. D.It caches the page on a CDN for next time
Show answer

Answer: A. It marks the update as non-urgent so urgent updates paint first

startTransition tells React the update can be interrupted, so urgent work like showing the pressed tab paints first. React can yield to the browser during the heavy render. It does not reduce total work, but it improves responsiveness.

React interview questions for experienced candidates hard

41. Why React.memo did not help

Child is wrapped in React.memo, but it still re-renders every time Parent renders. What is the most likely reason?

function Parent() {
  const [n, setN] = useState(0)
  const options = { sort: "asc" }
  return <Child options={options} onPick={() => setN(n + 1)} />
}
  1. A.React.memo only works with class components
  2. B.Child must use useEffect to block renders
  3. C.options and onPick are new objects on every render, so the shallow prop comparison fails
  4. D.useState always re-renders all children regardless of memo
Show answer

Answer: C. options and onPick are new objects on every render, so the shallow prop comparison fails

React.memo compares props by reference. An object literal and an inline arrow function are recreated on each render, so they are never equal to the previous ones. Moving the constant object outside the component, or using useMemo and useCallback, keeps references stable. The React Compiler can also add this memoisation automatically.

42. Resetting selection when items change

React’s docs allow a guarded setState during render for one pattern. Which code below is that pattern?

  1. A.useEffect(() => setSelection(null)) with no dependency array
  2. B.setSelection(null) at the top of the component every render
  3. C.selection = null; inside the render body
  4. D.if (items !== prevItems) { setPrevItems(items); setSelection(null) }
Show answer

Answer: D. if (items !== prevItems) { setPrevItems(items); setSelection(null) }

Storing the previous prop and updating state when it changes, guarded by a condition, lets React re-render immediately before children see stale state. It is better than an effect, which would render once with the old selection. A key reset is often simpler still.

43. Setting state to the same value

count is 5. What does setCount(5) do?

  1. A.It always re-renders the whole tree below it
  2. B.React bails out without re-rendering children
  3. C.It throws an error for a repeated value
  4. D.It resets count to its initial value
Show answer

Answer: B. React bails out without re-rendering children

If the new value is the same as the current one by Object.is, React skips re-rendering the component’s children and effects. React may still call the component once before bailing out, but that result is thrown away.

44. Items added twice in development

In development, each ADD dispatch adds the item twice. In production it is fine. What is the bug?

function reducer(state, action) {
  if (action.type === "ADD") {
    state.items.push(action.item)
    return { ...state }
  }
  return state
}
  1. A.dispatch is called twice by the button handler
  2. B.It mutates state, and StrictMode calls it twice
  3. C.Spreading state copies items twice
  4. D.Reducers must not use if statements
Show answer

Answer: B. It mutates state, and StrictMode calls it twice

StrictMode calls reducers twice in development to catch impure ones. Because push mutates the shared items array, the second call pushes again. Return a new array instead: { ...state, items: [...state.items, action.item] }.

45. use() that suspends forever

This client component keeps showing the Suspense fallback and refetches in a loop. Why?

function Orders() {
  const orders = use(fetch("/api/orders").then((r) => r.json()))
  return <List items={orders} />
}
  1. A.use() only works inside class components
  2. B.fetch is not allowed in React components
  3. C.Every render makes a brand-new promise
  4. D.use() needs a dependency array to cache
Show answer

Answer: C. Every render makes a brand-new promise

use() suspends until the promise resolves, then React re-renders the component. Here each render creates a brand-new promise, so it suspends again. Create the promise outside render, for example in a Server Component, a cache or a data library, and pass it in.

46. Errors an error boundary misses

An error boundary wraps the checkout page. Which error does it NOT catch?

  1. A.An error thrown while rendering a child
  2. B.An error thrown in a child’s useEffect
  3. C.An error thrown inside an onClick handler
  4. D.An error thrown in a child’s lifecycle method
Show answer

Answer: C. An error thrown inside an onClick handler

Error boundaries catch errors during rendering, in lifecycle methods and in effects of their children. Errors in event handlers happen outside rendering, so handle them with try/catch and set error state, which can then be thrown during render if needed.

47. Which fallback shows

Only Reviews suspends while its data loads. What does the user see?

<Suspense fallback={<PageSpinner />}>
  <ProductInfo />
  <Suspense fallback={<ReviewsSkeleton />}>
    <Reviews />
  </Suspense>
</Suspense>
  1. A.ProductInfo, with ReviewsSkeleton for Reviews
  2. B.PageSpinner covering the whole page
  3. C.A blank page until Reviews has loaded
  4. D.ProductInfo and PageSpinner together
Show answer

Answer: A. ProductInfo, with ReviewsSkeleton for Reviews

A suspending component shows the fallback of its nearest Suspense boundary. Reviews is inside the inner boundary, so only that part shows ReviewsSkeleton while ProductInfo renders normally. This lets pages reveal content in pieces.

48. Cleanup from a ref callback

In React 19, a ref callback starts a ResizeObserver on a node. What is the new way to stop it when the node is removed?

<div ref={(node) => {
  const ro = new ResizeObserver(onResize)
  ro.observe(node)
  /* ? */
}} />
  1. A.React calls the callback again with the same node
  2. B.Return a cleanup function: return () => ro.disconnect()
  3. C.Add a useEffect with an empty array to disconnect
  4. D.Nothing; observers stop when the node unmounts
Show answer

Answer: B. Return a cleanup function: return () => ro.disconnect()

Since React 19, a ref callback can return a cleanup function, which React calls when the element is detached. Before that, React called the callback with null instead. Observers keep running unless you disconnect them.

49. Two socket connections in development

In development, the server logs two WebSocket connections from one page load. The effect is useEffect(() => { connect() }, []). What does this point to?

  1. A.React StrictMode is a bug and should be disabled
  2. B.The dependency array should list connect
  3. C.The effect has no cleanup that closes the socket
  4. D.WebSockets cannot be opened in effects
Show answer

Answer: C. The effect has no cleanup that closes the socket

StrictMode mounts, unmounts and remounts in development. With a proper cleanup that closes the socket, you would see connect, disconnect, connect. Without it, the first socket leaks, which would also happen in production on real remounts.

50. Logging the latest theme from an effect

An effect connects to a room and, on "connected", shows a toast in the current theme. Adding theme to the deps reconnects whenever the theme changes. What fits best in React 19.2+?

  1. A.Leave theme out and disable the lint rule
  2. B.Store theme in state inside the effect
  3. C.Put the toast call in useMemo
  4. D.Read theme in a useEffectEvent called from the effect
Show answer

Answer: D. Read theme in a useEffectEvent called from the effect

useEffectEvent creates a function that always sees the latest props and state but is not a dependency. The effect re-runs only for roomId, while the event function reads the current theme when it fires. Silencing the linter would show a stale theme.

51. Streaming HTML with Suspense

With streaming server rendering, a page has a slow Recommendations section inside a Suspense boundary. What does the user get?

  1. A.Nothing until every part has loaded on the server
  2. B.The page shell first, then the section when ready
  3. C.The full page, but Recommendations is never server-rendered
  4. D.A client-side spinner for the whole page
Show answer

Answer: B. The page shell first, then the section when ready

React streams HTML in chunks. Everything outside the pending boundary is sent first with the fallback in place, and when the slow data resolves the server streams that section’s HTML plus a small script to swap it in.

52. Putting a function in useState

A developer wants to store a formatter function in state and writes setFormatter(formatINR). formatINR is a function taking a number. What goes wrong?

  1. A.Functions cannot be stored in state at all
  2. B.setFormatter ignores function arguments
  3. C.It works exactly as intended
  4. D.React calls formatINR as an updater with the old state
Show answer

Answer: D. React calls formatINR as an updater with the old state

When a setter receives a function, React treats it as an updater and calls it with the previous state, storing its return value. To store the function itself, wrap it: setFormatter(() => formatINR). The same applies to useState(() => fn).

53. A transition around typing

A developer wraps setQuery(e.target.value) for a controlled input in startTransition. Typing now feels broken. Why?

  1. A.startTransition only works with useReducer
  2. B.Transitions disable onChange handlers
  3. C.Controlled input values need urgent updates
  4. D.startTransition makes updates synchronous
Show answer

Answer: C. Controlled input values need urgent updates

Transition updates can be delayed or interrupted, but the input must reflect each keystroke at once. Keep setQuery urgent and put the expensive part, such as the filtered list, in a transition or behind useDeferredValue.

54. One user lookup per request

In Server Components, the header, sidebar and page all call getUser(id). How do you make it run once per server request?

  1. A.Wrap getUser with cache from react
  2. B.Wrap getUser with useMemo
  3. C.Store the user in a module-level variable
  4. D.Call getUser in a useEffect
Show answer

Answer: A. Wrap getUser with cache from react

React’s cache() memoises a function for the duration of one server request, so repeated calls with the same arguments share the result. A module-level variable would leak data between users across requests, and hooks do not work in Server Components.

55. Mocking APIs in component tests

Tests mock fetch with jest.fn() returning fixed objects, but they miss bugs in headers and error handling. What is a more realistic approach?

  1. A.Call the real production API in every test
  2. B.Intercept requests at the network level with MSW
  3. C.Skip tests for components that fetch
  4. D.Mock React itself so components never fetch
Show answer

Answer: B. Intercept requests at the network level with MSW

Mock Service Worker intercepts real fetch requests and returns responses you define, including status codes and delays. Components use their real fetching code, so tests catch wrong URLs, headers and error paths, and the same handlers work in the browser.

56. Jumping to row 3,000 in a FlatList

scrollToIndex(3000) on a FlatList of fixed 72px rows is slow or fails. What makes it fast?

  1. A.Wrapping each row in its own ScrollView
  2. B.Setting initialNumToRender to 5,000 rows
  3. C.Using the row index as the key for each row
  4. D.getItemLayout with length 72, offset 72 × index
Show answer

Answer: D. getItemLayout with length 72, offset 72 × index

Without getItemLayout, FlatList must render items to measure them before it knows where row 3,000 is. If rows have a fixed height, getItemLayout gives the length and offset directly, so jumps are instant and less measuring work is done.

57. Animation stutters while data loads

A React Native card animation stutters whenever a large JSON response is parsed. What is the usual fix?

  1. A.Run it on the UI thread (native driver or Reanimated)
  2. B.Use setInterval to drive the animation each frame
  3. C.Parse the JSON inside the animation callback instead
  4. D.Increase the animation duration so it hides the lag
Show answer

Answer: A. Run it on the UI thread (native driver or Reanimated)

JS-driven animations need the JavaScript thread every frame, so heavy JS work drops frames. useNativeDriver: true or Reanimated worklets run the animation on the UI thread, so it stays smooth even when JS is busy.

58. Hydration mismatch with time

A server-rendered React component shows the current time and the console reports a hydration mismatch. What is the cause and a correct fix?

export function LastUpdated() {
  return <span>{new Date().toLocaleTimeString()}</span>
}
  1. A.toLocaleTimeString is not supported on the server; use a polyfill
  2. B.React cannot render span elements during SSR; use a div
  3. C.The component must be marked async
  4. D.Server and browser render different text (different time and time zone); render a stable value on the server, or show the time only after mount in a Client Component
Show answer

Answer: D. Server and browser render different text (different time and time zone); render a stable value on the server, or show the time only after mount in a Client Component

Hydration requires the first client render to match the server HTML. Time and locale differ between server and browser, so the text differs. Render a fixed timestamp passed from the server, or set the value in useEffect after mount; suppressHydrationWarning is only for small, known cases.

59. Search results from an old query

A search screen fetches on every keystroke. Typing "pen" quickly sometimes shows results for "pe". What fixes this race?

  1. A.Ignore or abort earlier requests in the effect cleanup
  2. B.Add a 50 ms setTimeout before each setState call
  3. C.Move the fetch into the render body of the screen
  4. D.Remove the dependency array from the useEffect
Show answer

Answer: A. Ignore or abort earlier requests in the effect cleanup

Responses can return out of order, so a slow "pe" response may land after the "pen" one. Using an AbortController or an ignore flag set in the effect cleanup ensures only the latest query updates state. Debouncing input also reduces the number of requests.

60. Resetting a form when the user changes

An EditProfile form keeps old typed values when the selected user changes from A to B. What is a clean way to reset all its internal state?

  1. A.Call useState again with the new user inside an if block
  2. B.Render it as <EditProfile key={userId} /> so a new user remounts it
  3. C.Store every field in a global variable and clear it
  4. D.Wrap the form in React.memo with no comparison function
Show answer

Answer: B. Render it as <EditProfile key={userId} /> so a new user remounts it

React keeps component state as long as the same component type stays at the same place with the same key. Changing the key tells React it is a different instance, so it unmounts the old one and mounts a fresh one with initial state. This avoids syncing effects that copy props into state.

Ready to test yourself?

Take a 15-question React MCQ test with a score at the end, or practise the full React question bank and earn points on the leaderboard.