React 200: Intermediate Field Guide
Elevate your skills with 100 intermediate-level React questions. Dive deep into Custom Hooks, Performance Optimization, Redux, Next.js SSR, and React 19 Concurrent patterns.
1. Advanced Hooks & Custom Hooks
Q1
How does useLayoutEffect differ from useEffect?
useLayoutEffect runs synchronously immediately after DOM mutations but *before* the browser paints the screen. It is used to measure DOM elements and mutate layout without causing a visible UI flicker, whereas useEffect runs asynchronously *after* the paint.Q2
What is the “stale closure” problem in React hooks?
A stale closure occurs when a callback function (like inside
useEffect or a timeout) captures old state variables from a previous render because the dependencies weren’t properly listed in the hook’s dependency array.Q3
When should you prefer useReducer over useState?
Use
useReducer when you have complex state logic involving multiple sub-values, or when the next state depends heavily on the previous state. It centralizes state transitions in a single reducer function, making testing and debugging easier.Q4
What is lazy initialization in useState, and when should you use it?
Lazy initialization is passing a function to
useState(initFn) instead of a value. It ensures the initialization logic only runs on the initial render, saving processing time if computing the starting state is computationally expensive.Q5
What is useSyncExternalStore used for?
It is a hook built to safely subscribe to external data sources (like Redux stores or browser APIs like
window.matchMedia) in a way that is compatible with Concurrent React, avoiding “tearing” (inconsistent UI) during renders.Q6
How do you test a custom hook that doesn’t return JSX?
You use tools like
renderHook from @testing-library/react. It creates a mock component wrapper around your hook so you can execute it, pass props to it, and assert against its returned values and side effects in isolation.Q7
What is the purpose of useImperativeHandle?
It is used in conjunction with
forwardRef to customize the instance value (the methods or properties) that is exposed to parent components when they place a ref on your custom component, effectively hiding internal DOM nodes while exposing specific APIs.Q8
Why shouldn’t you mutate refs directly during the render phase?
The render phase in React should be pure without side effects. Mutating
ref.current during render makes the component’s output unpredictable and breaks Concurrent Mode capabilities; mutations should happen inside useEffect or event handlers.Q9
How can you force a React component to re-render without changing its props or main state?
You can bind a dummy state variable and update it, usually implemented as a custom hook like
const [, forceRender] = useReducer(x => x + 1, 0). Calling forceRender() triggers a re-render.Q10
What is the best practice for fetching data inside a custom hook?
Abstract the fetch logic inside a
useEffect, manage loading, error, and data states internally, and utilize an AbortController in the cleanup function to cancel the fetch if the component unmounts before the request finishes.2. Context API & State Management
Q11
What is the primary performance pitfall of the Context API?
Whenever a Context Provider’s value changes, *all* components consuming that context will forcefully re-render, even if they only need a tiny slice of the data. This can cause severe performance issues in large component trees.
Q12
How can you optimize renders when using Context?
Split contexts by domain or rate of change (e.g., separate
ThemeContext from UserContext), or separate state from dispatch actions (e.g., UserValueContext and UserDispatchContext) so dispatch consumers don’t re-render on state changes.Q13
When would you choose Redux over Context API?
Choose Redux for complex, high-frequency state updates, heavy reliance on middleware (like sagas/thunks), time-travel debugging, and when you need granular selector optimizations to prevent unnecessary re-renders. Context is better for simple dependency injection.
Q14
What is Redux Toolkit (RTK)?
RTK is the official, opinionated toolset for Redux. It drastically reduces boilerplate by utilizing “slices”, automatically wiring up the Redux DevTools, integrating Immer for mutable-style state updates, and providing RTK Query for data fetching.
Q15
What is the difference between Redux Thunks and Sagas?
Thunks are simple functions allowing you to return asynchronous functions instead of action objects. Sagas use ES6 Generators to handle complex async flows, race conditions, and background tasks in a highly testable, but steeper learning curve, manner.
Q16
What is Zustand, and how does it compare to Redux?
Zustand is a minimalist state manager that uses hooks. Unlike Redux, it doesn’t require Context Providers wrapping your app, has minimal boilerplate, and allows components to subscribe to partial state changes effortlessly to avoid over-rendering.
Q17
How do Recoil and Jotai differ from Context and Redux?
They are “atomic” state managers. Instead of a single monolithic store (Redux) or top-down providers (Context), state is broken into independent atoms that can be placed anywhere in the tree. Components subscribe only to the specific atoms they use.
Q18
Is Prop Drilling inherently an anti-pattern?
No, prop drilling is the most direct and explicit way to share state. It only becomes an anti-pattern when it passes through many layers of components that don’t need the data, making the codebase brittle and hard to refactor.
Q19
Can you consume a React Context outside of a React component (e.g., inside an API utility file)?
No. Context relies on React’s component tree and hook architecture. To share data with non-React files, you must use an external state manager (like Redux or Zustand stores, which exist outside the tree) or pass the context value as an argument to the utility function.
Q20
How do you persist global state across page reloads?
You must synchronize your state with browser storage (localStorage or sessionStorage). Libraries like Redux Persist or Zustand’s
persist middleware handle this automatically by serializing state to storage on change, and hydrating it on load.3. Performance Optimization
Q21
How does React.memo determine if a component should re-render?
By default,
React.memo performs a shallow comparison of all current and previous props. If the references to the props haven’t changed, React skips re-rendering the component and reuses the last rendered result.Q22
How do you implement a custom comparison in React.memo?
You can pass a custom comparison function as the second argument:
React.memo(Component, arePropsEqual). It should return true if props are equivalent (skipping render) and false if they differ (triggering render).Q23
Why does passing an inline object or function break React.memo?
Inline objects (
style={{color: 'red'}}) or functions (onClick={() => {}}) create new memory references on every parent render. Shallow comparison sees them as “new” props, causing the memoized component to re-render anyway.Q24
When is using useMemo counterproductive?
useMemo has overhead. Using it on trivial calculations (like concatenating strings or mapping a small array) costs more in memory allocation and dependency checking than it saves in computation. Use it only for genuinely expensive operations.Q25
What is Windowing (or Virtualization) in React?
It is a technique (using libraries like
react-window) to render only the items currently visible in the user’s viewport, rather than thousands of DOM nodes at once. This keeps scrolling smooth and memory usage low for massive lists.Q26
What is the React Profiler API?
The
<Profiler> component (and the DevTools Profiler tab) measures how often a React application renders and what the “cost” of rendering is. It helps identify performance bottlenecks and unnecessary renders in the tree.Q27
How do you identify and fix memory leaks in React?
Memory leaks usually happen when setting state on unmounted components (e.g., from lingering timeouts or unfinished API calls). Fix them by returning a cleanup function in
useEffect that cancels subscriptions or utilizes an AbortController.Q28
How does React.lazy help with performance?
React.lazy enables code splitting at the component level. It delays the downloading and parsing of large component bundles (like heavy charts or secondary routes) until the user actually navigates to them, drastically speeding up initial page load.Q29
What is a “render phase” vs “commit phase”?
The Render phase is where React calls components to calculate the Virtual DOM diff (this can be interrupted in concurrent mode). The Commit phase is where React actually applies those calculated changes to the browser’s DOM (this is synchronous and cannot be interrupted).
Q30
How does decoupling state minimize re-renders?
If you place state at the top of a large layout, any change renders the whole tree. Decoupling means pushing state down to the lowest possible component (e.g., putting an input’s state inside the input component) so only that small node re-renders on keystrokes.
4. Component Patterns & Architecture
Q31
What is a Higher-Order Component (HOC)?
An HOC is a function that takes a component and returns a new, enhanced component (e.g.,
withAuth(Dashboard)). It was the primary way to reuse component logic before hooks, though hooks have largely replaced them.Q32
What is the Render Props pattern?
It’s a pattern where a component takes a function that returns a React element as a prop (often the
children prop). The parent manages state and passes it to this function, delegating the rendering logic to the consumer.Q33
What is the Compound Components pattern?
It’s a pattern where multiple components work together to form a cohesive UI, sharing implicit state via Context (e.g.,
<Menu>, <Menu.Item>). It provides a highly flexible, declarative API for complex UI widgets.Q34
What is the Control Props pattern?
This pattern allows users to pass “controlled” state props to a component (like a standard HTML input) while allowing the component to manage its own internal state if the props are omitted, providing maximum flexibility.
Q35
Are Container vs Presentational components still relevant?
While the strict separation of “Smart Container” (data fetching) and “Dumb Presentational” (UI) components is less rigid thanks to hooks, the underlying principle of separating business logic from UI rendering remains a best practice.
Q36
What are React Portals?
ReactDOM.createPortal allows you to render a component’s children into a different part of the DOM tree (like a div directly under the body tag), breaking out of CSS overflow: hidden limitations. Essential for modals, tooltips, and dropdowns.Q37
What is the “Slots” pattern in React?
Instead of just using
children, you pass React components into named props (e.g., <Layout leftSidebar={<Nav/>} header={<Header/>} />). This is highly effective for complex layouts that need specific elements injected into strict template areas.Q38
How do you build a Recursive Component?
A recursive component calls itself inside its own render function. It is necessary for rendering deeply nested data structures of unknown depth, like a file system tree or nested JSON, ensuring you include a base case (stop condition) to prevent infinite loops.
Q39
What is a Polymorphic Component?
It’s a component that can render as different HTML tags based on a prop (usually
as or component). For example, <Button as="a" href="..."> renders an anchor tag but applies the button’s visual styling.Q40
Why should you avoid defining components inside other components?
Defining a component inside another causes it to be completely recreated in memory on every parent render. React treats it as a brand new component type, forcing it to unmount and remount, destroying its internal state and trashing performance.
5. React Router & Navigation
Q41
What is the difference between useNavigate and useLocation?
useNavigate returns a function allowing you to programmatically trigger a route change (e.g., redirecting after a login). useLocation returns an object representing the current URL, useful for reading pathnames or trigger effects on route change.Q42
How do URL Parameters differ from Query Parameters?
URL Parameters are dynamic segments of the path itself (e.g.,
/users/:id), accessed via useParams(). Query Parameters are key-value pairs at the end of the URL (e.g., ?sort=asc), accessed via useSearchParams().Q43
How do you implement Protected Routes?
Create a wrapper component (e.g.,
<RequireAuth>) that checks the user’s authentication state via context. If authenticated, it renders its children or an <Outlet />; if not, it returns a <Navigate to="/login" replace />.Q44
What is the purpose of the <Outlet /> component?
In React Router v6,
<Outlet /> is placed inside a parent route component to indicate exactly where the matching child route components should be rendered, enabling seamless nested layouts (like sidebars with changing content panes).Q45
What are React Router Data Loaders?
Introduced in v6.4, `loader` functions allow you to fetch data required for a route *before* the route renders, eliminating the “render, then fetch, then render again” loading spinners common in standard useEffect fetching.
Q46
What are React Router Actions?
Paired with loaders, `action` functions handle data mutations (like POST/PUT requests from form submissions). React Router automatically revalidates active loaders after an action completes, keeping UI data in sync.
Q47
How do you create a Catch-all (404) route?
Define a route at the bottom of your routing configuration with a path of asterisk:
<Route path="*" element={<NotFound />} />. React Router will match this only if no other explicitly defined routes match the URL.Q48
When should you use programmatic navigation over declarative?
Use declarative
<Link> components for user-driven navigation (like menus or buttons) for accessibility. Use programmatic navigate() only as a side-effect of a business logic action, like successful form submissions or timeouts.Q49
What is the difference between Link and NavLink?
<NavLink> is a special version of <Link> that inherently knows whether or not it is “active” based on the current URL. It allows you to easily apply active CSS classes to navigation menu items.Q50
Why use BrowserRouter over HashRouter?
BrowserRouter uses the HTML5 History API for clean URLs (e.g., `/about`), but requires a backend configured to handle client-side routing. HashRouter adds a hash (e.g., `/#/about`) ensuring the backend ignores the route, useful for static file hosts without configuration.6. Error Handling & Boundaries
Q51
What is an Error Boundary in React?
An Error Boundary is a React component that catches JavaScript errors anywhere in its child component tree, logs those errors, and displays a fallback UI instead of crashing the whole component tree.
Q52
Why must Error Boundaries be written as Class Components?
Currently, there are no Hook equivalents for the lifecycle methods
componentDidCatch and static getDerivedStateFromError. You must use a class component or rely on third-party libraries that provide wrapper components.Q53
What errors do Error Boundaries NOT catch?
Error Boundaries only catch errors during rendering, in lifecycle methods, and in constructors. They do *not* catch errors inside event handlers (like onClick), asynchronous code (setTimeout/fetch), server-side rendering, or errors within the boundary itself.
Q54
How do you handle errors inside event handlers?
Since Error Boundaries don’t catch event handler errors, you must use standard JavaScript
try/catch blocks inside the event function and manually set error state to show a warning to the user.Q55
What does static getDerivedStateFromError do?
It is invoked after an error is thrown in a descendant component. It receives the error and should return a value to update the boundary’s state (e.g.,
hasError: true) so the next render phase displays the fallback UI.Q56
What does componentDidCatch do?
It is invoked after an error has been thrown and is used to log the error information (the error and the component stack trace) to external error tracking services like Sentry or LogRocket.
Q57
What is the “react-error-boundary” library?
It is a popular open-source library that provides a flexible
<ErrorBoundary> component and a useErrorHandler hook, allowing functional component developers to implement advanced boundaries without writing class components.Q58
How do you reset an Error Boundary?
You provide a way to clear the error state (e.g., resetting
hasError to false). Libraries like react-error-boundary offer a resetErrorBoundary function that can be tied to a “Try Again” button in the fallback UI.Q59
Can you have multiple Error Boundaries in one app?
Yes, it is highly recommended. Wrap granular features (like a sidebar, or an individual widget) in their own boundaries. If a widget crashes, only that section displays a fallback, keeping the rest of the application functional.
Q60
How do you force async errors to be caught by an Error Boundary?
You can catch the async error in a
try/catch block and pass it to a custom state setter function. Setting state with a function that throws the error will cause React to catch it during the subsequent render phase, triggering the boundary.7. Data Fetching & Asynchronous UI
Q61
Why are libraries like React Query or SWR preferred over useEffect for fetching?
They automatically handle complex challenges like caching, background revalidation, deduplication of multiple requests, retry logic on failure, and pagination—things that require dozens of lines of bug-prone boilerplate in raw useEffects.
Q62
What is a race condition in useEffect data fetching?
It occurs when a component sends multiple async requests (e.g., changing search terms quickly), but responses arrive out of order. The UI might end up displaying the result of the first request instead of the most recent one.
Q63
How do you solve race conditions in useEffect?
Use a cleanup function with a boolean flag (e.g.,
let isMounted = true, set to false on cleanup) to ignore responses if the component unmounted, or better, use an AbortController to physically cancel the outdated network request.Q64
What is React Suspense used for in data fetching?
Suspense lets components “wait” for something before they render. Supported data-fetching libraries can suspend rendering while fetching, allowing a parent
<Suspense fallback={<Loader/>}> to cleanly show loading states without internal state tracking.Q65
What is Optimistic UI updating?
It’s a pattern where the UI is updated immediately under the assumption that a server request (like a ‘Like’ button) will succeed. If the server eventually returns an error, the UI rolls back to the previous true state, ensuring a snappy UX.
Q66
How does SWR handle data caching?
SWR (Stale-While-Revalidate) returns cached, potentially stale data immediately to render the UI instantly, while simultaneously sending a background request to fetch updated data and mutating the UI when the fresh data arrives.
Q67
How do you implement Infinite Scroll?
Track the user’s scroll position via the Intersection Observer API. When the user nears the bottom of the list element, trigger a fetch for the next “page” of data and append it to the existing state array.
Q68
How do WebSockets integrate into React?
Initialize the WebSocket connection inside a
useEffect with an empty dependency array. Bind the socket’s onmessage event to update React state, and ensure you close the socket inside the useEffect cleanup function.Q69
What is the benefit of Apollo Client for GraphQL in React?
Apollo Client provides customized hooks (
useQuery, useMutation) that normalize and cache GraphQL data in memory, meaning if two components ask for the same entity by ID, they seamlessly share the exact same cached object.Q70
Why should you debounce search inputs?
If you fetch data onChange, typing “React” triggers 5 rapid API calls. Debouncing delays the state update and API call until the user has stopped typing for a specified duration, vastly reducing server load and preventing race conditions.
8. React 19 Intermediate Features
Q71
How does useTransition work?
useTransition allows you to mark specific state updates as “non-urgent” (transitions). React will render the urgent updates (like typing in an input) immediately, while rendering the transition (like filtering a massive list) in the background.Q72
What is the difference between useTransition and useDeferredValue?
useTransition wraps the state setter function to defer the work, useful when you control the state update. useDeferredValue wraps the state value itself, useful when you receive data as a prop and cannot control how the state was set.Q73
How does Concurrent Rendering handle interruptions?
In Concurrent mode, React can start rendering a heavy non-urgent task, pause it if a high-priority user interaction occurs (like a click), render the urgent interaction, and then resume or restart the heavy task without locking the browser.
Q74
How does the `use()` hook handle Promises?
When you pass a Promise to
use(Promise), it suspends the component until the Promise resolves. It replaces the need for older fetching boilerplate, seamlessly integrating asynchronous data streams with React Suspense boundaries.Q75
What is Document Metadata hoisting in React 19?
React 19 natively identifies elements like
<title> and <meta> rendered deeply within any component and automatically extracts/hoists them into the document <head>, eliminating the need for libraries like React Helmet.Q76
How do Server Actions work with Progressive Enhancement?
Because Server Actions integrate natively with HTML
<form action> props, standard forms can submit data directly to the server action before the client-side JavaScript bundle has even finished loading (Progressive Enhancement).Q77
What are the preload and prefetchDNS APIs?
They are manual optimizations allowing developers to instruct the browser to proactively fetch scripts, stylesheets, or resolve domain names via React, reducing latency when users transition to features requiring those assets.
Q78
How do you handle refs in function components in React 19?
You simply pass
ref as a standard destructured prop (e.g., function MyInput({ ref, placeholder })). The old `forwardRef` HOC is now redundant and primarily kept for legacy compatibility.Q79
How does useOptimistic handle rollbacks?
You pass real state into
useOptimistic. While an async action runs, it returns an optimistic value. When the async action completes or fails, the component automatically snaps back to whatever the base truth state dictates, handling rollbacks instantly.Q80
How does the React Compiler alter the use of memoization hooks?
The React Compiler analyzes data flow during the build step and automatically memoizes values and callbacks where necessary. It effectively deprecates manual usage of
useMemo and useCallback, significantly cleaning up component code.9. Testing React Apps
Q81
What is the philosophy behind React Testing Library (RTL)?
RTL enforces testing components the way users interact with them (e.g., finding buttons by their text, reading ARIA labels) rather than testing internal implementation details like state values or component instances.
Q82
What is the difference between getBy, queryBy, and findBy in RTL?
getBy throws an error if an element is missing (good for asserting existence). queryBy returns null if missing (good for asserting non-existence). findBy returns a Promise, making it essential for awaiting elements that appear asynchronously.Q83
How should you test API calls in React components?
Instead of mocking
fetch or axios directly, use Mock Service Worker (MSW). MSW intercepts actual network requests at the network level and returns mock data, testing your components under highly realistic conditions.Q84
How do you test a component that requires a Context Provider?
You must wrap the component inside the required Provider during the test render. This is often centralized by creating a custom
render function in RTL that automatically wraps tested components in all necessary app providers.Q85
What is the purpose of act() in React testing?
act() ensures all React updates (rendering, state updates, effects) triggered by an event are fully completed and flushed to the DOM before your assertions run, avoiding warnings and flaky tests.Q86
What are the pros and cons of Snapshot Testing?
Pros: Quick to write, alerts you to unexpected UI changes. Cons: Highly brittle, fails on minor safe changes, and developers often blindly update snapshots without checking, rendering them effectively useless for deep logic verification.
Q87
Why prefer UserEvent over fireEvent in RTL?
fireEvent simply dispatches a raw DOM event. userEvent simulates full user interactions (like typing), which triggers the precise sequence of events (focus, keydown, keypress, keyup, input) that a real browser would, ensuring higher fidelity tests.Q88
How do you test asynchronous state updates?
Trigger the action, then use RTL’s
waitFor or findBy* queries to pause the test runner until the expected DOM change (like a loading spinner disappearing and data appearing) has completed.Q89
What does 100% Code Coverage mean, and is it a good goal?
It means every line of code was executed during tests. While a good metric, 100% coverage often leads to writing meaningless tests just to hit the number. Focusing on testing critical user flows and edge cases is far more valuable.
Q90
How does E2E testing differ from Component Integration testing?
E2E (Cypress/Playwright) boots a real browser, connects to real databases, and clicks through the app exactly as a user would. Integration testing (RTL) renders components in a simulated DOM (JSDOM) with mocked APIs for faster, isolated feedback.
10. SSR, Next.js & Ecosystem
Q91
What is Server-Side Rendering (SSR) vs Client-Side Rendering (CSR)?
In CSR, the browser downloads a blank HTML page and JS renders the UI. In SSR, the server runs React to generate fully populated HTML per request, sending it to the browser for faster initial visibility and better SEO.
Q92
What is Static Site Generation (SSG)?
The server renders the React pages into static HTML files exactly once during the build process. These files are served instantly via CDNs, offering incredible performance, but require a rebuild if the data changes.
Q93
What is Incremental Static Regeneration (ISR) in Next.js?
ISR allows you to update static pages in the background *after* deployment. A user gets a fast static cached page, but Next.js rebuilds the page behind the scenes and serves the fresh page to the next visitor.
Q94
What is Hydration?
Hydration is the process where React attaches event listeners and state management to the static HTML previously sent by the server. It “wakes up” the dead HTML, turning it into a fully interactive React application.
Q95
What causes a Hydration Mismatch error?
It happens when the initial HTML rendered by the server does not identically match the HTML React expects to see on the first client render. Common causes include relying on `window` data, local time zones, or invalid HTML nesting.
Q96
What is the difference between Next.js App Router and Pages Router?
Pages Router maps URLs to files in the `pages` directory using traditional components. App Router utilizes the modern React architecture, mapping URLs to folders, and natively defaulting every component to a React Server Component.
Q97
When do you use the “use client” directive?
In Server Component ecosystems (like App Router), everything runs on the server by default. You add `”use client”` to the top of components that require browser APIs, state (`useState`), or interactivity (`onClick`).
Q98
How do React Server Components differ from traditional SSR?
Traditional SSR sends rendered HTML, but still ships the component’s JavaScript bundle to the client for hydration. Server Components execute *only* on the server, meaning zero JavaScript is sent to the client for that component, drastically reducing bundle size.
Q99
Why do plain React SPAs struggle with SEO?
Crawlers see an empty `<div id=”root”></div>` when they request the page. While Googlebot can execute JS to eventually read the rendered content, it is slower and less reliable than serving pre-rendered HTML metadata from the server.
Q100
How do you handle Auth state safely in SSR applications?
Since the server doesn’t have access to the browser’s `localStorage`, authentication tokens must be stored in secure, HttpOnly Cookies. The server parses the cookie to determine auth state while rendering the initial HTML.