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))
}, [])- A.Add width to the dependency array
- B.Use a passive event listener instead
- C.Store the handler in a variable and return a cleanup that calls removeEventListener with it
- 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?
- A.Copy user into every layer’s own useState
- B.Pass <Avatar user={user} /> down as children
- C.Store user on window and read it in Avatar
- 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?
- A.Each call to a custom hook gets its own separate state
- B.Custom hooks can only be used in one component
- C.useCart must be wrapped in memo
- 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?
- A.const id = Math.random().toString()
- B.const id = "field"
- C.let counter++ in a module variable
- 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?
- A.Button.defaultProps = { size: "md" }
- B.if (!props.size) props.size = "md"
- C.useState("md") inside Button
- 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])- A.useMemo never caches arrays
- B.filters is a new object each render
- C.items must be listed last
- 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?
- A.Children stop re-rendering completely
- B.The page re-renders twice as often
- C.Handlers stop working after the first click
- 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?
- A.Move the state into a small button component
- B.Wrap the whole page component in useMemo
- C.Store isHovered in a global module variable
- 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?
- A.add (e) => onKey(e); return () => remove (e) => onKey(e)
- B.add onKey in the effect; remove it in a separate effect
- C.const h = (e) => onKey(e); add h; return () => remove h
- 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?
- A.Import ProductDetails inside the Tabs file
- B.Pass <ProductDetails /> into Tabs as children
- C.Add "use client" to ProductDetails as well
- 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?
- A.It runs tests without needing any DOM
- B.It is synchronous, so no await is needed
- C.It simulates real key presses and focus
- 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?
- A.screen.getByText("Welcome, Ravi", { wait: true })
- B.setTimeout(() => screen.getByText(...), 1000)
- C.await screen.findByText("Welcome, Ravi")
- 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?
- A.The props are equal, so Row skips re-rendering
- B.Row must re-render now
- C.React throws away the old props and result
- 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?
- A.HOCs are no longer allowed at all in React 19
- B.Hooks run on a separate thread from rendering
- C.Hooks cache the data between different users
- 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?
- A.Focus returns to the button that opened the modal
- B.Focus moves to the browser address bar
- C.Focus moves to the footer
- 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?
- A.TanStack Query never refetches
- B.The copy is a second source of truth that drifts
- C.useState cannot hold arrays that came from a server
- 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?
- A.Nothing is needed; React escapes HTML automatically
- B.Sanitize the HTML with an allowlist sanitizer like DOMPurify, plus a CSP
- C.Strip every <script> tag from the bio with a regular expression before saving it
- 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?
- A.The app crashes because read is not allowed in build
- B.The provider is disposed after every frame
- C.The widget will not rebuild when userProvider changes
- 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]);- A.useEffect ignores dependency arrays in React Native
- B.fetchProducts is async, so effects always rerun
- C.filters is a new object every render, so it always differs
- 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?
- A.It marks the update as non-urgent so urgent updates paint first
- B.It moves the rendering work into a web worker automatically for you
- C.It skips rendering the new tab content entirely
- 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.