React Interview Questions for Experienced Developers

React interview questions for developers with 2 to 5 years experience: memoisation, effects done properly, state management choices, routing, testing and TypeScript.

59 questions · 2-5 years experience

At two to five years the questions stop being "what is" and become "when would you". You are expected to have opinions and to be able to defend them with something that happened on a real project.

The most common way to fail this round is not being wrong. It is answering a design question with a definition. If you are asked whether you would use context or Redux and you explain what context is, you have not answered.

What changes at this level

  • Trade-offs, not definitions. Every answer should end with the condition that would change your mind.
  • Debugging. Expect "this component re-renders on every keystroke, how would you find out why". The answer starts with the Profiler, not with React.memo.
  • Your own codebase. How you structure a project, how you test, how you handle data fetching and errors.
  • A take-home or a longer coding round. Usually a small feature with an API, marked on state handling, loading and error states, and whether you left the code readable.

The questions, with one line answers

Say each one out loud before you open it. If the sentence does not come out cleanly, follow the link for the full answer and the reasoning behind it.

  1. The function you return from an effect. React runs it before the effect runs again, and once more when the component unmounts, so every subscription, timer or listener is undone exactly once.

    React HooksRead the full answer

  2. useEffect runs asynchronously after the browser has painted. useLayoutEffect runs synchronously after the DOM is updated but before paint, so it can measure and adjust layout without the user seeing a flicker.

    React HooksRead the full answer

  3. useReducer holds state and updates it by dispatching actions to a reducer function. Prefer it when several state values change together, when the next state depends on the previous one in non-trivial ways, or when the update logic is worth testing on its own.

    React HooksRead the full answer

  4. useMemo caches the result of a calculation between renders and recomputes it only when its dependencies change. It is worth using for genuinely expensive computations, and for keeping an object or array reference stable so a memoised child or an effect does not fire needlessly.

    React HooksRead the full answer

  5. useMemo caches the value a function returns; useCallback caches the function itself. useCallback(fn, deps) is exactly useMemo(() => fn, deps).

    React HooksRead the full answer

  6. useRef returns a mutable object with a .current property that survives re-renders and never triggers one. It is used to reach a DOM node, and to store a mutable value that the UI does not display, such as a timer id or the previous value of a prop.

    React HooksRead the full answer

  7. A custom hook is a function whose name starts with use and which calls other hooks. Write one when the same stateful logic appears in more than one component, or when a component's logic is complex enough that naming it makes the component readable.

    React HooksRead the full answer

  8. useId generates a unique, stable id that matches between the server render and the client hydration. It exists for accessibility attributes such as linking a label to an input, not for keys or database ids.

    React HooksRead the full answer

  9. Measure first with the Profiler and a Lighthouse run, then fix what you found: cut the bundle with code splitting, cut unnecessary renders with memoisation and better state placement, and cut the work per render with virtualisation.

    PerformanceRead the full answer

  10. React.memo wraps a component so React skips re-rendering it when its props are shallowly equal to last time. It does nothing when the props include a new object, array or function each render, or when the component re-renders because of its own state or context.

    PerformanceRead the full answer

  11. State that lives too high in the tree, new object or function references passed as props, context values that change identity, and missing memoisation on expensive children. You find them with the React DevTools Profiler and the "highlight updates" option.

    PerformanceRead the full answer

  12. Code splitting breaks the bundle into chunks loaded on demand instead of one file up front. React.lazy takes a dynamic import and returns a component; Suspense renders a fallback while that chunk downloads.

    PerformanceRead the full answer

  13. Virtualisation renders only the rows currently visible plus a small buffer, and reuses them as the user scrolls. You need it once a list is large enough that the number of DOM nodes, not React, is the bottleneck — typically from a few hundred rows.

    PerformanceRead the full answer

  14. Record an interaction in the React DevTools Profiler tab and read the commits: the flamegraph shows what rendered and how long it took, and each component shows why it re-rendered. There is also a Profiler component for measuring in code.

    PerformanceRead the full answer

  15. Because it creates a new function identity on every render, which breaks the shallow prop comparison in React.memo and re-triggers any effect that depends on it. On a plain DOM element it costs nothing worth worrying about.

    PerformanceRead the full answer

  16. Local useState, lifted state, context, a client store such as Redux Toolkit or Zustand, and a server state library such as TanStack Query. Choose by asking who needs the value, how often it changes, and whether it came from a server.

    State ManagementRead the full answer

  17. Not on its own. Context is a way to transport a value down the tree without prop drilling. It has no opinion on how state is stored or updated, and no re-render optimisation, so it is dependency injection rather than state management.

    State ManagementRead the full answer

  18. Redux is a predictable state container. Its three principles are a single source of truth, state that is read only and changed only by dispatching actions, and changes described by pure reducer functions.

    State ManagementRead the full answer

  19. Redux Toolkit is the official, batteries-included Redux package. createSlice generates actions and reducers together, Immer lets you write mutating syntax that produces immutable updates, and the store comes preconfigured with thunks and devtools.

    State ManagementRead the full answer

  20. Context passes a value down the tree and re-renders every consumer when it changes. Redux is a full state container with selector based subscriptions, middleware and devtools, so components re-render only when the slice they select actually changes.

    State ManagementRead the full answer

  21. TanStack Query, formerly React Query, is an async state manager for server data. It caches responses by key, deduplicates in-flight requests, revalidates in the background, and gives you loading, error and stale states without writing them yourself.

    State ManagementRead the full answer

  22. One useEffect covers all three: an empty dependency array is mount, a populated array is update, and the returned cleanup function is unmount. The mapping is not exact, because effects synchronise rather than fire on lifecycle events.

    Lifecycle & EffectsRead the full answer

  23. Track the first render with a ref and return early. There is no built-in option, and needing this often means the logic belongs in an event handler instead.

    Lifecycle & EffectsRead the full answer

  24. Because React 18's StrictMode mounts, unmounts and remounts every component in development to check that your effects clean up after themselves. It does not happen in production.

    Lifecycle & EffectsRead the full answer

  25. By default yes: React re-renders the whole subtree. Re-rendering means calling the component function and diffing, not touching the DOM, so it is usually cheap. React.memo skips a child whose props are unchanged.

    Lifecycle & EffectsRead the full answer

  26. In practice, with a data library such as TanStack Query, RTK Query or the framework's loader. If you must do it by hand, use an effect keyed on the request inputs, handle loading and error states, and cancel the request in the cleanup.

    Lifecycle & EffectsRead the full answer

  27. shouldComponentUpdate lets a class return false to skip a re-render. PureComponent implements it for you with a shallow comparison of props and state. React.memo is the function component equivalent.

    Lifecycle & EffectsRead the full answer

  28. Composition means building a component by passing other components into it, usually through children or named props, instead of extending a base component. React recommends it because a component's output is data, so passing that data around is more flexible than inheriting behaviour.

    Components & PropsRead the full answer

  29. Prop drilling is passing a prop through components that do not use it, just to reach a descendant that does. You avoid it with component composition, with context, or with a state library, in that order.

    Components & PropsRead the full answer

  30. A higher order component is a function that takes a component and returns a new component wrapping it with extra behaviour. It was the pre-hooks way of sharing non visual logic between components.

    Components & PropsRead the full answer

  31. A render prop is a prop whose value is a function returning JSX. The component owns some behaviour or state and hands it to the caller, who decides what to draw with it.

    Components & PropsRead the full answer

  32. A ref is an escape hatch to a DOM node or a mutable value that survives renders without causing one. forwardRef lets a parent attach a ref to a DOM node inside a child component, because refs are not passed through as ordinary props.

    Components & PropsRead the full answer

  33. It splits components into containers that fetch data and hold state, and presentational components that only take props and render. Hooks made the strict split much less necessary, but the underlying idea of keeping rendering separate from data access is still good practice.

    Components & PropsRead the full answer

  34. An error boundary is a component that catches errors thrown while rendering its subtree and shows a fallback instead of unmounting the whole app. It does not catch errors in event handlers, in asynchronous code, during server rendering, or thrown by the boundary itself.

    Advanced ReactRead the full answer

  35. createPortal renders children into a DOM node outside the parent's hierarchy while keeping them in the React tree. You need it when an ancestor's overflow, z-index or transform would clip or trap a modal, tooltip or dropdown.

    Advanced ReactRead the full answer

  36. Suspense lets a component tell React it is not ready, and React shows the nearest boundary's fallback until it is. It powers lazy loaded components, and with a data source that integrates with it, data fetching too.

    Advanced ReactRead the full answer

  37. CSR renders in the browser from an empty shell. SSR renders HTML per request. SSG renders HTML at build time. ISR is SSG that regenerates pages in the background after a set interval.

    Advanced ReactRead the full answer

  38. Catch them where they happen. Wrap event handlers and async work in try/catch and move the failure into state, use the query library's error state for data fetching, and add window listeners for error and unhandledrejection as a last resort.

    Advanced ReactRead the full answer

  39. A SyntheticEvent is React's cross-browser wrapper around the native event. It has the same interface — preventDefault, stopPropagation, target — but behaves identically across browsers.

    Forms & EventsRead the full answer

  40. Validate on submit and on blur rather than on every keystroke, keep errors in state keyed by field, and put the rules in a schema so the same rules can run on the server. In practice, use React Hook Form with Zod rather than writing it by hand.

    Forms & EventsRead the full answer

  41. React's onChange fires on every keystroke, like the native input event. The native change event only fires when the field loses focus. React deliberately normalised this so onChange means "the value changed".

    Forms & EventsRead the full answer

  42. Missing label associations, using a div with onClick instead of a button, error messages that are not linked to their field or announced, and losing focus after a dynamic change.

    Forms & EventsRead the full answer

  43. Server routing asks the server for a new HTML document on every navigation. Client routing intercepts the click, changes the URL with the History API and swaps components in place, so no document is fetched.

    React RouterRead the full answer

  44. Nested routes let a parent route render shared layout and a child route render inside it. Outlet is the placeholder in the parent where React Router renders whichever child matched.

    React RouterRead the full answer

  45. Wrap the routes in a component that checks authentication and either renders an Outlet or redirects to the login page, remembering where the user was trying to go.

    React RouterRead the full answer

  46. Wrap each route's component in React.lazy with a dynamic import and put a Suspense boundary above the routes, so each route's code downloads only when the user first visits it.

    React RouterRead the full answer

  47. React Testing Library renders components and queries them the way a user would, through visible text and accessible roles. Enzyme exposed the component instance, so tests could reach into state and internal methods, which made them break on every refactor.

    TestingRead the full answer

  48. Accessible queries first — getByRole, then getByLabelText, getByPlaceholderText and getByText — then getByTestId as a last resort. The order mirrors how a user, including one with a screen reader, finds things.

    TestingRead the full answer

  49. Use findBy queries or waitFor, both of which retry until the assertion passes or the timeout expires. Never assert immediately after an action that triggers an async update, and never reach for a fixed setTimeout.

    TestingRead the full answer

  50. fireEvent dispatches a single DOM event. userEvent simulates the full interaction a real user produces — for a click that is pointer, mouse down, focus, mouse up and click — so it catches bugs fireEvent walks straight past.

    TestingRead the full answer

  51. Intercept at the network layer with Mock Service Worker rather than stubbing fetch or the module that calls it. The component then runs its real data code and the test stays valid when you change HTTP client.

    TestingRead the full answer

  52. Either works. Interfaces can be merged and extended and give slightly nicer errors for object shapes; type aliases can express unions, intersections and mapped types. Most teams pick one for consistency, and type is the more common default in React code.

    React + TypeScriptRead the full answer

  53. React.FC is a type for function components that annotates the whole function rather than its props. Teams moved away from it because it used to add an implicit children prop, it complicates generic components, and annotating the parameter is simpler.

    React + TypeScriptRead the full answer

  54. Use React's generic event types parameterised by the element: React.ChangeEvent<HTMLInputElement>, React.MouseEvent<HTMLButtonElement>, React.FormEvent<HTMLFormElement>. Inline handlers usually infer these for you.

    React + TypeScriptRead the full answer

  55. For a DOM node use useRef<HTMLInputElement>(null), which gives a read-only current that may be null. For a mutable value use useRef<number | undefined>(undefined), which gives a writable current.

    React + TypeScriptRead the full answer

  56. Keep the input controlled for instant feedback, debounce the value with a custom hook, and fire the request from an effect keyed on the debounced value with cancellation in the cleanup.

    Coding ChallengesRead the full answer

  57. Hold data, error and status in one state object, fetch in an effect keyed on the URL, abort in the cleanup, and return the triple. Say out loud that in production you would use a query library.

    Coding ChallengesRead the full answer

  58. Store the value in a ref and update it in an effect. Because effects run after render, the ref still holds the previous render's value while the component renders.

    Coding ChallengesRead the full answer

  59. Take the current page, total pages and a change handler as props, keep the page in the URL rather than in local state, and render a window of page numbers with ellipses rather than every page.

    Coding ChallengesRead the full answer

Where to spend your time

  1. [Hooks](/react-interview-questions/hooks) in depth. useMemo versus useCallback, dependency arrays, stale closures. These come up in every interview at this level.
  2. [Performance](/react-interview-questions/performance). Be able to describe a Profiler session in detail.
  3. [State management](/react-interview-questions/state-management). Practise the client state versus server state distinction until it is automatic.
  4. [Testing](/react-interview-questions/testing). Even if your current team does not test much, have a position on what is worth testing.
  5. [TypeScript](/react-interview-questions/typescript). Most roles at this level are TypeScript roles.

Go deeper on a topic