React Senior Interview Questions

Real-world interview questions for Senior React Developers, covering Hooks, Architecture, Performance, and Advanced Patterns.

⚛️ React Core & Architecture

1. Explain the Virtual DOM and how Reconciliation works.

The Virtual DOM is a lightweight JavaScript object that is a copy of the real DOM. When state changes, React creates a new Virtual DOM tree. Reconciliation is the process where React compares the new tree with the old one (Diffing). It calculates the minimum number of changes needed and applies them to the real DOM in a batch.

đź§  Memory tip: Virtual DOM = Blueprint; Reconciliation = Finding the difference; Commit = Updating real DOM.

2. Why are "Keys" important in lists, and why is `index` a bad key?

Keys give elements a stable identity. They help React know which items were changed, added, or removed. If you use index as a key and the list sort order changes, React effectively sees “Item 0 changed” instead of “Item 0 moved to position 2.” This causes unnecessary re-renders and can even corrupt local component state (like inputs losing focus or showing wrong values).

đź§  Memory tip: Keys = ID cards for elements. No ID = Identity crisis during re-sort.

3. What is the difference between specific Class components and Functional Components provided by the mental model?

Class Components are about instances. this is mutable and persists over time. Functional Components are about snapshots. Every render is an independent function call with its own props and state constant for that specific render. Hooks allow functional components to “hook into” React state and lifecycle features without needing a class instance.

🧠 Memory tip: Class = “Use this (Time travel)”; Function = “Capture the moment (Snapshot)”.

4. Explain Synthetic Events in React.

React doesn’t attach event listeners to every single DOM node. Instead, it uses Event Delegation. It attaches one single event listener to the root of the app. When you click a button, the event bubbles up to the root, and React’s wrapper (SyntheticEvent) handles it. This smooths over browser inconsistencies (e.g. attachEvent vs addEventListener) and improves memory usage.

🧠 Memory tip: Synthetic Event = React’s polished wrapper over the browser’s messy native events.

5. What is "Lifting State Up"?

If two sibling components need access to the same data, you move the state up to their closest common ancestor. The ancestor passes the state down as Props to both siblings. This ensures a “Single Source of Truth.”

🧠 Memory tip: Siblings can’t talk directly; they talk through their Parent.

6. What is the difference between React Element and React Component?
  • React Element: A plain JS object describing what you want to see on the screen. It is cheap to create. e.g. <div /> or <MyComponent />.
  • React Component: A function (or class) that accepts props and returns React Elements.

🧠 Memory tip: Element = Object (The “What”); Component = Function (The “Factory”).

7. What are Portals and when do you use them?

Portals let you render a child node into a DOM node that exists outside of the parent component’s hierarchy. Common use case: Modals, Tooltips, or Popovers. Even though it lives outside in the DOM (e.g., attached to document.body), it still behaves like a normal React child (events bubble up, Context works).

đź§  Memory tip: Teleport the HTML div, but keep the React Logic connection.

8. Explain Error Boundaries.

They are React components that catch JavaScript errors anywhere in their child component tree. They log those errors, and display a fallback UI instead of the component tree that crashed. Crucial Note: As of now, Error Boundaries must be Class Components. There is no Hook equivalent yet.

🧠 Memory tip: The “Try-Catch” block for your Component Tree.


🪝 Hooks & State Management

1. `useEffect`: dependencies and lifecycle equivalents.

useEffect replaces componentDidMount, componentDidUpdate, and componentWillUnmount.

  • []: Runs once (Mount).
  • [prop]: Runs on Mount + when prop changes (Update).
  • return () => ...: Runs when component unmounts or before re-running effect (Cleanup/Unmount).

đź§  Memory tip: Effect = Side effects synchronized with State changes.

2. `useState` vs `useReducer` - when to choose which?
  • useState: Best for independent primitives (strings, booleans, simple numbers).
  • useReducer: Best for complex state logic where the next state depends on the previous one, or you have deep objects. It follows the Redux pattern (dispatching actions).

đź§  Memory tip: Simple toggle = useState; Complex form wizard = useReducer.

3. Why typically avoid putting Objects in `useEffect` dependency arrays?

In JS, objects are compared by reference. { id: 1 } === { id: 1 } is false. If you pass an object literal or a new object created in the render body to the dependency array, useEffect will run on every single render, causing infinite loops or performance hits. Fix: Use useMemo to stabilize the object, or pass specific primitive properties (user.id).

đź§  Memory tip: Objects are new every render; Primitives are stable.

4. What is the "Stale Closure" problem with Hooks?

If you access a state variable inside a useEffect or useCallback but forget to add it to the dependency array, the Hook “remembers” the old value from the first render. It creates a closure around the variable at the time of creation. Fix: Always include all used variables in the dependency array (or use the ESLint plugin to help you).

🧠 Memory tip: The function is stuck in the past if you don’t tell it the future (deps).

5. Context API vs Redux/Zustand?
  • Context: Great for low-frequency updates shared globally (Theme, User Auth, Language). Downside: If the context value changes, all consumers re-render, usually optimizing performance requires splitting contexts.
  • Redux/Zustand: Great for high-frequency updates or complex state flows. They have selectors that allow components to subscribe only to slices of the state, preventing unnecessary re-renders.

đź§  Memory tip: Context for static globals; Redux for dynamic/complex data flows.

6. What is `useLayoutEffect` vs `useEffect`?
  • useEffect: Runs asynchronous, after the paint. The user sees the old screen, then the new screen updates. (Good for API calls, subscriptions).
  • useLayoutEffect: Runs synchronous, before the browser paints. (Good for measuring DOM elements or preventing flickering UI).

đź§  Memory tip: Use useEffect 99% of the time. Use useLayoutEffect only if UI updates flicker.


⚙️ Performance Optimization

1. When should you actually use `useMemo` and `useCallback`?

Don’t use them everywhere! They have a cost (memory allocation + comparison check). Use them only when:

  1. Referential Equality: You are passing a function/object as a prop to a React.memo component (so it doesn’t break memoization).
  2. Expensive Calculation: We are filtering a list of 10,000 items.

đź§  Memory tip: Premature optimization is the root of evil. Measure first.

2. How does `React.memo` work?

It is a Higher Order Component meant for functional components. It checks the props. If the props haven’t changed (shallow comparison), it skips re-rendering the component and reuses the last result. It works like PureComponent in class-based React.

🧠 Memory tip: Memo = “Memorize this output if inputs (props) are same.”

3. What is Code Splitting and `React.lazy`?

Instead of sending one giant JS bundle (5MB) to the user, we split it. React.lazy allows you to dynamically import a component only when it is needed. const Settings = React.lazy(() => import('./Settings')); You must wrap it in <Suspense fallback={<Spinner />}> to handle the loading state.

🧠 Memory tip: Don’t load the Admin Dashboard for a Landing Page visitor.

4. How do you handle large lists (1000+ items)?

Virtualization (or Windowing). Instead of rendering 1000 DOM nodes, you only render the 10 that are currently visible on screen. Libraries like react-window or react-virtualized handle this naturally. It keeps the DOM light and scrolling smooth (60fps).

đź§  Memory tip: Only render what the user sees relative to the viewport.

5. What causes a React component to re-render?
  1. State Change: useState setter called.
  2. Props Change: Parent passes new props.
  3. Parent Re-render: If parent renders, children render (recursively), unless stopped by memo.
  4. Context Change: If a Context Provider value changes, all consumers re-render.

đź§  Memory tip: State, Props, Parent, Context.


đź§© Patterns & Best Practices

1. What are Custom Hooks and why use them?

Custom hooks are JavaScript functions that start with use and call other hooks. They are the primary mechanism for logic reuse in React. Instead of duplicating “Fetch Data” logic in 5 components, you extract it to useFetch. It separates the Logic (Hook) from the View (Component).

đź§  Memory tip: Components share UI; Hooks share Logic.

2. Explain the "Render Props" pattern (vs Hooks).

Before Hooks, this was popular. A component takes a function as a prop (often called render or children) and calls it, passing internal state arguments. e.g. <MouseTracker render={mouse => <Cat position={mouse} />} />. Nowadays, Custom Hooks generally replace this pattern as they are cleaner and avoid “Wrapper Hell.”

🧠 Memory tip: Inversion of Control: “I manage state, you decide what to render.”

3. Higher Order Components (HOC) - what are they?

A function that takes a component and returns a new component. const EnhancedComponent = withAuth(ProfileComponent); Used for cross-cutting concerns (Auth, Logging). Again, mostly replaced by Hooks, but still seen in libraries (like Redux connect or withRouter).

đź§  Memory tip: Component Decorators. Wrappers that add super-powers.

4. Controlled vs Uncontrolled Components?
  • Controlled: React manages the state. The input value is set via props (value={state}) and updated via change handler (onChange={setState}). Single source of truth.
  • Uncontrolled: The DOM manages the state. You use a ref (inputRef.current.value) to pull the value when needed (like on Submit).

đź§  Memory tip: Controlled = React drives; Uncontrolled = DOM drives.

5. Prop Drilling and how to avoid it?

Prop Drilling is passing data through many layers of components (Grandparent -> Parent -> Child -> Grandchild) just to get it to the bottom. Avoid it by:

  1. Composition: Pass components as children (<Layout header={<UserMenu />} />).
  2. Context: Teleport data directly to the bottom.
  3. State Management: Use Redux/Zustand.

🧠 Memory tip: Don’t play “Telephone” with your props.


🚀 Advanced & Ecosystem

1. What are React Server Components (RSC)?

RSC allows components to run exclusively on the server. They can directly access DBs or file systems. They send zero bundle size to the client (the browser receives the rendered result, not the JS code). They solve the problem of client-server waterfalls and bundle bloat. They cannot use useState or useEffect.

đź§  Memory tip: Component logic that stays on the server; Zero JS cost for the client.

2. What is Suspense?

Suspense lets you declaratively “wait” for something (like code loading or data fetching) and show a fallback (spinner) while waiting. It decouples the fetching logic from the loading state UI. Suspense is the boundary that catches the “promise” thrown by children.

🧠 Memory tip: “Show this Spinner while my children are getting ready.”

3. Explain "Hydration" in SSR.

SSR sends pure HTML first (fast paint). Hydration is the process where React JS downloads, runs, and “attaches” event listeners to that existing HTML. It creates the internal fiber tree to match the DOM. If the server HTML matches the client tree, it’s successful. If not, you get a “Hydration Mismatch” error.

🧠 Memory tip: “Watering” the dry HTML with JavaScript to make it alive (interactive).

4. What is Strict Mode?

A development-only tool. It renders components twice to help find bugs:

  1. Unsafe lifecycles.
  2. Unexpected side effects (detecting impure render functions).
  3. Deprecated API usage. It does not run in production.

đź§  Memory tip: The annoying double-render that saves you from bugs later.

5. What is the difference between `npx create-react-app` and Vite?
  • CRA: Uses Webpack. It bundles the entire app before starting the dev server. Slow startup on large apps. (Deprecated).
  • Vite: Uses ES Modules (native browser imports) in dev. Instant startup. Uses Rollup for production build. Much faster and the modern standard.

đź§  Memory tip: Webpack = Bundle all then serve (Slow); Vite = Serve instantly (Fast).