React interview questions and answers in 2026 go well past "what is JSX?" Interviewers expect you to reason about hooks as synchronization, when useMemo helps vs hurts, how Server Components change data fetching, and how you would build a search bar or todo list under time pressure. React JS interview questions and answers loops at product companies blend fundamentals, architecture judgment, and React coding interview questions—live components, state fixes, and performance debugging.
Below are 45 questions with elaborate explanations with elaborate answers; technical sections include a strong answer sample you can say aloud. For dedicated CSS interview questions and html css interview questions (layout, cascade, responsive design), see CSS interview questions. For TypeScript interview questions (props typing, generics, narrowing), see TypeScript interview questions. For interview questions on React JS for experienced developers and scenario-based senior prep, see interview questions on React JS for experienced developers. Pair with front end developer interviews for HTML/CSS/browser fundamentals, full stack developer interviews for API integration, Node.js interviews for backend pairing, and Angular interviews when interviewers compare frameworks.
Tested on: Ubuntu 25.04 (Plucky Puffin); kernel 6.14.0-37-generic; Node.js 20.18.2.
Interview context and how to prepare
What do React interviews actually test?
React interviews test whether you can build interactive UIs that stay correct as state and data change—not whether you memorized every API from 2018.
| Layer | What interviewers probe |
|---|---|
| Fundamentals | Components, JSX, props, state, one-way data flow |
| Hooks | useState, useEffect, dependencies, custom hooks |
| Rendering | Reconciliation, keys, memoization judgment |
| State & data | Local vs server state, Context, TanStack Query |
| Performance | Re-render causes, useMemo/useCallback, code splitting |
| Modern React | Suspense, transitions, RSC, React 19 Actions |
| Coding | Small components live—forms, lists, fetch patterns |
| Level | Emphasis |
|---|---|
| Junior | Hooks basics, props, simple lists/forms |
| Mid | Effects, routing, testing, API integration |
| Senior | Architecture, performance, RSC, accessibility, system trade-offs |
React coding interview questions vs theory — what is different?
Theory rounds ask definitions—Virtual DOM, lifecycle in a hooks world, controlled inputs.
Coding rounds ask you to ship in 30–45 minutes:
| Common exercise | What they watch |
|---|---|
| Todo / filter list | State shape, keys, immutability |
| Search with debounce | Effects, cleanup, stale closures |
| Fetch and display | Loading/error, abort, query library mention |
| Form validation | Controlled fields, submit handling |
| Fix a buggy component | Missing deps, mutating state, wrong key |
Strong candidates think aloud, state assumptions, and write tests or edge-case handling when time allows.
What is a typical React interview loop?
| Round | Duration | Focus |
|---|---|---|
| Recruiter / HM | 30 min | Projects, stack, team UI standards |
| JavaScript fundamentals | 45 min | Closures, async, DOM—see front end prep |
| React deep dive | 45–60 min | Hooks, rendering, state architecture |
| Live coding | 45–90 min | Component exercise, debug session |
| System design (senior) | 45 min | Frontend architecture, data layer, perf |
| Behavioral | 30 min | Collaboration, incidents, code review |
Take-home UIs often include README trade-offs and test coverage—not only pixels.
What is a realistic 4–6 week prep plan?
| Week | Focus | Output |
|---|---|---|
| 1 | Core React — JSX, props, state, lists, forms | Rebuild todo without tutorial |
| 2 | Hooks depth — effects, refs, custom hooks | useDebounce, useFetch patterns |
| 3 | Data — TanStack Query, error boundaries | List page with loading/error/empty |
| 4 | Performance & testing — RTL, memo judgment | Fix one unnecessary re-render case |
| 5 | React 18/19 — Suspense, transitions, RSC basics | Explain client vs server component split |
| 6 | Coding drills + mock | 3 timed exercises; 2 STAR stories |
Build one small app (dashboard, catalog, or admin table) you can demo and defend.
React fundamentals
What is React and why use it?
React is a JavaScript library for building user interfaces with a component-based model and declarative rendering—you describe UI as a function of state, React updates the DOM when state changes.
| Benefit | Detail |
|---|---|
| Components | Reusable, composable UI units |
| Declarative UI | Less manual DOM patching |
| Ecosystem | Routing, state, meta-frameworks (Next.js, Remix) |
| Talent pool | Widely adopted in product companies |
React is a library, not a full framework—you choose routing, data layer, and styling.
A strong answer is:
React lets teams compose UIs from components with predictable one-way data flow; I use it when rich client interactivity and a large ecosystem matter more than a batteries-included monolith framework.
What is JSX?
JSX is syntax sugar that looks like HTML inside JavaScript. Build tools (Babel, SWC) transform it to React.createElement calls.
function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
}Rules interviewers mention:
- One parent element or Fragment (
<>...</>) classNameinstead ofclass- JavaScript expressions in
{curly braces} - Self-closing tags required (
<img />)
JSX is not HTML—attributes and casing follow React conventions.
A strong answer is:
JSX is declarative syntax compiled to JavaScript function calls—it keeps UI structure readable while staying in the JavaScript type and tooling world.
What is the Virtual DOM and how does reconciliation work?
React keeps a lightweight tree representing UI. On state change:
- React calls your component functions to produce a new element tree
- Diffs new tree vs previous (reconciliation)
- Applies minimal DOM updates
Fiber (React 16+) enables incremental work, priorities, and concurrent rendering—reconciliation can pause and resume.
| Concept | Interview point |
|---|---|
| Not magic speed | Avoids naive full-page reflows; big lists still need keys and memoization |
| Keys | Help match list items across renders |
| Batched updates | React 18 auto-batches state updates in async handlers |
A strong answer is:
Virtual DOM is React's in-memory UI representation; reconciliation diffs trees and patches the real DOM—keys and component structure determine whether updates are efficient.
What is the difference between props and state?
| Props | State | |
|---|---|---|
| Source | Parent passes down | Owned inside component (or hook) |
| Mutability | Read-only for child | Updated via setter (setState, useState) |
| Purpose | Configuration, callbacks | UI that changes over time |
One-way data flow: parent state → child props → events call parent setters.
Anti-pattern: child mutates props object directly.
A strong answer is:
Props are inputs from parents; state is local mutable UI data updated immutably through setters—data flows down, events flow up.
Controlled vs uncontrolled components?
| Type | Who owns input value |
|---|---|
| Controlled | React state drives value + onChange |
| Uncontrolled | DOM holds value; read via ref |
// Controlled
const [email, setEmail] = useState("");
<input value={email} onChange={(e) => setEmail(e.target.value)} />
// Uncontrolled
const inputRef = useRef(null);
<input ref={inputRef} defaultValue="" />Prefer controlled for validation, instant feedback, and predictable tests. Uncontrolled fits simple forms or file inputs.
A strong answer is:
Controlled inputs bind value to state for validation and single source of truth; uncontrolled defer to the DOM and refs when that is simpler.
Hooks and component behavior
Explain useState — how does React schedule updates?
useState returns [value, setter]. Updates are scheduled—React may batch multiple setters in one render.
const [count, setCount] = useState(0);
function increment() {
setCount((c) => c + 1); // functional update — safe when next state depends on previous
}| Pattern | When |
|---|---|
setCount(5) |
Absolute new value |
setCount(c => c + 1) |
Depends on previous state |
Lazy init useState(() => expensive()) |
Heavy initial computation once |
State updates are asynchronous—do not assume count changes immediately after setCount.
A strong answer is:
useState stores local UI data; I use functional updates when the next value depends on the previous and remember setters batch for performance.
Explain useEffect — dependencies, cleanup, and pitfalls.
useEffect synchronizes your component with external systems—fetch, subscriptions, DOM APIs—not "lifecycle replacement" only.
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then((r) => r.json())
.then(setData)
.catch(handleError);
return () => controller.abort(); // cleanup on dep change or unmount
}, [url]);| Pitfall | Fix |
|---|---|
| Missing deps | Include values used inside; or move stable logic out |
| Stale closure | Functional updates or correct deps |
| No cleanup | Leaks timers/subscriptions |
| Strict Mode double invoke (dev) | Effects must be idempotent; cleanup must work |
Prefer TanStack Query or framework data loaders over raw fetch effects for server data in 2026 interviews.
A strong answer is:
useEffect runs after paint to sync with externals—I declare honest dependencies, always clean up subscriptions and abort fetches, and reach for query libraries instead of hand-rolled fetch effects when data is server-backed.
What are the Rules of Hooks?
- Only call hooks at the top level — not inside loops, conditions, or nested functions
- Only call hooks from React functions — components or custom hooks
Why: React relies on call order to associate state with each hook instance.
Breaking rules causes "Rendered more hooks than previous render" bugs—impossible to debug casually.
Custom hooks must also follow these rules and name with use prefix.
A strong answer is:
Hooks run in a fixed order every render—I never call them conditionally and I extract reusable logic into custom hooks instead of copying stateful patterns.
When do useCallback and useMemo help — and when are they premature optimization?
| Hook | Caches |
|---|---|
useMemo |
Expensive computation result |
useCallback |
Stable function reference |
Helpful when:
- Passing callbacks to
React.memochildren that would re-render on reference change - Heavy derived data (large list filtering) proven slow in profiler
Not needed for every inline function—memoization has memory cost.
const visible = useMemo(() => items.filter((i) => i.active), [items]);
const onSelect = useCallback((id) => setSelected(id), []);Interview trap: wrapping everything in useCallback without memoized children buys nothing.
A strong answer is:
I use useMemo and useCallback when profiling shows wasted child renders or expensive derivations—not by default on every handler.
What is useRef used for?
useRef holds a mutable box that persists across renders without triggering re-render when .current changes.
| Use | Example |
|---|---|
| DOM access | Focus input, measure element |
| Mutable instance value | Timer id, previous prop value |
| Avoid stale closures | Store latest callback in ref |
const inputRef = useRef(null);
useEffect(() => inputRef.current?.focus(), []);Unlike state, updating ref.current does not schedule render.
A strong answer is:
useRef stores mutable values and DOM nodes across renders without causing re-renders—I use it for focus, timers, and escaping stale closure traps in effects.
When should you use useContext?
Context shares values deep in the tree without prop drilling—theme, locale, auth snapshot.
| Good fit | Poor fit |
|---|---|
| Low-frequency updates | High-frequency changing data (re-renders all consumers) |
| Theme, i18n, auth user | Entire app state store |
Pattern:
const ThemeContext = createContext("light");
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}For complex client state, consider Zustand, Redux Toolkit, or colocated state + query library.
A strong answer is:
Context is for stable global values like theme or auth—I avoid putting fast-changing data in context because it re-renders every consumer.
What are custom hooks? Implement a useDebounce pattern.
Custom hooks extract stateful logic reusable across components—they must follow Rules of Hooks.
Debounce logic (testable without React runtime):
function debounce(fn, delayMs) {
let timer;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delayMs);
};
}
let callCount = 0;
const debounced = debounce(() => { callCount += 1; }, 50);
debounced();
debounced();
debounced();
setTimeout(() => {
console.log(callCount);
}, 100);When you click Run, after a short wait you should see 1 printed once—multiple rapid calls collapse into one execution after the delay.
In React, pair debounced values with useEffect to drive API search—cleanup clears timeout on unmount.
A strong answer is:
Custom hooks share stateful behavior—I extract debounce, fetch, and media-query logic into
use*functions so components stay declarative.
State management and data fetching
Local state vs global state vs server state?
| Category | Examples | Tooling |
|---|---|---|
| Local | Form field, modal open | useState |
| Shared client | Wizard steps, UI preferences | Context, Zustand, Redux |
| Server | API records, pagination | TanStack Query, SWR, RSC |
Colocation principle: keep state as low as possible until multiple distant components need it.
Server state is async, cached, and shared—different from client UI toggles. Do not copy server lists into Redux by default in 2026 greenfield apps.
A strong answer is:
Local state for UI, global client libraries only when prop drilling hurts, and dedicated server-state tools for API data with cache and invalidation.
Why use TanStack Query instead of useEffect for fetching?
useEffect fetch |
TanStack Query |
|---|---|
| Manual loading/error flags | Built-in states |
| No cache deduplication | Shared cache per query key |
| Refetch logic reinvented | Stale time, retry, invalidation |
| Race conditions manual | Request deduping |
const { data, isLoading, error } = useQuery({
queryKey: ["orders", page],
queryFn: () => fetchOrders(page),
});Interviewers want you to mention query keys, stale-while-revalidate, and mutations with optimistic updates.
A strong answer is:
TanStack Query handles caching, deduplication, and refetch policies—I use effects for true externals like subscriptions, not routine GET requests.
When is Redux Toolkit still justified?
Redux shines when:
- Many features read/write shared client state with traceable updates
- You need devtools, time-travel debugging, middleware
- Complex cross-feature workflows (multi-step wizards, offline sync)
Often overkill for apps where server state dominates—Query + light Context/Zustand covers many products.
Redux Toolkit reduces boilerplate with slices and createAsyncThunk—know it even if you prefer simpler stores.
A strong answer is:
I reach for Redux when client state is complex and many modules need predictable updates; otherwise Query plus minimal local/global state is enough.
What is lifting state up?
When two siblings need the same data, move state to their common parent and pass props down + callbacks up.
Parent (owns `filter`)
/ \
List FilterBarAlternative: shared store or URL search params for bookmarkable filter state.
A strong answer is:
I lift state to the lowest common ancestor when siblings must stay in sync, or use URL/query state when users should share or bookmark it.
Composition vs inheritance in React?
React favors composition—pass children or render props instead of deep class hierarchies.
function Card({ title, children }) {
return (
<section>
<h2>{title}</h2>
{children}
</section>
);
}Patterns: compound components, slots, higher-order components (less common now), custom hooks (preferred reuse in hooks era).
A strong answer is:
I compose behavior with children and hooks rather than subclassing components—composition keeps trees flexible and types clearer.
Performance, rendering, and lists
What does React.memo do?
React.memo wraps a component to skip re-render if props are shallow-equal to last render.
Requires stable props—inline object/function props defeat memo unless parent uses useMemo/useCallback.
Use when profiler shows expensive pure presentational children re-rendering often.
A strong answer is:
React.memo avoids re-rendering when props are unchanged—I pair it with stable callbacks only after profiling proves child render cost matters.
Why are keys important in lists?
Keys help React identify which items changed, were added, or removed.
| Key choice | Result |
|---|---|
| Stable unique id | Correct reuse of component state |
| Array index | Breaks on reorder/insert—avoid when list mutates |
Wrong keys cause state bugs (wrong row stays checked) and inefficient DOM updates.
A strong answer is:
Keys must be stable per entity— I use database ids, not array indexes, for dynamic lists so React matches the right row across renders.
What causes unnecessary re-renders and how do you debug them?
Common causes:
| Cause | Fix |
|---|---|
| Parent re-rendered | memo, split state down |
| New object/array literal props | Memoize or pass primitives |
| Context value recreated | Memoize provider value |
| Global store too coarse | Selectors, split contexts |
Tools: React DevTools Profiler, why-did-you-render (dev), logging render counts.
A strong answer is:
I profile first, then fix the actual source—unstable props, fat context, or state living too high—not blanket memo everywhere.
What is code splitting and lazy loading in React?
Code splitting loads JavaScript chunks on demand—smaller initial bundle.
const AdminPanel = lazy(() => import("./AdminPanel"));
function App() {
return (
<Suspense fallback={<Spinner />}>
<AdminPanel />
</Suspense>
);
}Route-based splitting (React Router, Next.js) is the most common production pattern.
A strong answer is:
I lazy-load heavy routes and admin surfaces behind Suspense so first paint ships only what most users need.
Why does useEffect run twice in development?
React Strict Mode intentionally double-invokes mounts, effects, and certain lifecycles in development to surface missing cleanup and side-effect bugs.
It does not run twice in production.
Your effect cleanup must abort fetches and clear timers—double invoke proves that works.
A strong answer is:
Strict Mode double-runs effects in dev on purpose—I write idempotent effects with proper cleanup so production and dev stay reliable.
Routing, forms, errors, and testing
How does client-side routing work with React Router?
React Router maps URL paths to component trees without full page reload.
| Concept | Role |
|---|---|
<Routes> / <Route> |
Path → element |
<Link> |
Declarative navigation |
useParams |
Dynamic segments (/users/:id) |
useSearchParams |
Query strings |
| Loaders (v6.4+) | Data before render |
Protect routes with auth wrappers—redirect unauthenticated users.
Pair with full stack interviews for end-to-end auth flows.
A strong answer is:
React Router keeps URL and UI in sync on the client—I use nested routes, loaders where appropriate, and guarded routes for auth.
What are error boundaries?
Error boundaries catch render errors in child tree and show fallback UI—they must be class components (still in 2026) or framework equivalents.
They do not catch:
- Event handler errors (use try/catch)
- Async errors outside render
- Errors in the boundary itself
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error, info) { log(error, info); }
render() {
return this.state.hasError ? <Fallback /> : this.props.children;
}
}A strong answer is:
Error boundaries isolate render failures so the whole app does not white-screen—I log to monitoring and still handle event and async errors separately.
How do React 19 Actions improve forms?
Actions are async functions passed to <form action={submitAction}> that integrate pending and error state without manual preventDefault boilerplate.
Related APIs:
| API | Role |
|---|---|
useActionState |
Form action return state |
useFormStatus |
Pending state in child buttons |
useOptimistic |
Instant UI while mutation completes |
async function saveOrder(prevState, formData) {
"use server"; // in Server Actions context (Next.js)
// validate and persist
return { ok: true };
}Even without Next.js, interviewers expect you to know Actions reduce hand-rolled loading flags.
A strong answer is:
React 19 Actions standardize form submissions with built-in pending and error handling—I prefer them over manual useState toggles for mutations when the stack supports them.
How do you test React components?
React Testing Library encourages tests that resemble user behavior:
import { render, screen, fireEvent } from "@testing-library/react";
test("increments counter", () => {
render(<Counter />);
fireEvent.click(screen.getByRole("button", { name: /increment/i }));
expect(screen.getByText("1")).toBeInTheDocument();
});| Layer | Tool |
|---|---|
| Unit | RTL + Vitest/Jest |
| Integration | MSW for API mocks |
| E2E | Playwright, Cypress |
Test behavior and accessibility roles—not implementation details like internal state variable names.
A strong answer is:
I test what users see and do with Testing Library, mock APIs with MSW, and reserve E2E for critical flows like checkout or login.
What React accessibility practices do interviews expect?
| Practice | Detail |
|---|---|
| Semantic HTML | button vs div onClick |
| Labels | htmlFor + id on inputs |
| Keyboard | Focus trap in modals, Escape to close |
| ARIA sparingly | Fix semantics first |
| Live regions | Announce async updates |
React does not fix a11y automatically—see front end interviews for broader WCAG depth.
A strong answer is:
I use semantic elements, label every control, manage focus in dialogs, and test with keyboard and screen reader checks—not bolt-on ARIA only.
React 18, React 19, and Server Components
What are concurrent features in React 18?
Concurrent rendering lets React interrupt low-priority updates to keep UI responsive.
| API | Use |
|---|---|
useTransition |
Mark non-urgent state updates (filter large list) |
useDeferredValue |
Defer rendering expensive derived value |
Suspense |
Wait for lazy code or async data with fallback |
const [isPending, startTransition] = useTransition();
startTransition(() => setFilter(text)); // keeps input snappyA strong answer is:
Concurrent features prioritize urgent UI like typing over heavy re-renders—I use transitions and Suspense to keep interactions smooth under load.
What are React Server Components (RSC)?
Server Components run only on the server—they never ship component JavaScript to the browser.
| Server Component | Client Component ("use client") |
|---|---|
| Fetch data directly | Uses hooks, event handlers |
| Zero client JS for that tree | Ships to browser |
| Can use server-only modules | Browser APIs allowed |
Not the same as SSR: SSR still hydrates client components; RSC sends rendered output without hydrating that component code.
Popular in Next.js App Router—know the boundary interview answer even if your team uses Vite SPA.
A strong answer is:
Server Components render on the server to shrink bundles and access data directly; client components handle interactivity behind the
use clientboundary.
What is the use() hook in React 19?
use() reads a Promise or Context during render—can be called conditionally (unlike other hooks).
Integrates with Suspense—component suspends until Promise resolves.
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map((c) => <p key={c.id}>{c.text}</p>);
}Prefer framework data patterns you can explain—RSC, Query, or use() with Suspense boundaries.
A strong answer is:
use() lets render read promises with Suspense—I use it where the framework supports streaming data, otherwise TanStack Query on the client.
SSR vs CSR vs SSG — when do you use each?
| Model | When |
|---|---|
| CSR | Dashboards behind auth, highly interactive SPA |
| SSR | SEO pages, fast first paint with dynamic data |
| SSG | Marketing docs, blogs with build-time content |
| ISR | Mix—static with periodic revalidation (Next.js) |
Hydration mismatch is a common production bug—server HTML must match client first render.
A strong answer is:
I pick rendering mode by SEO, personalization, and interactivity—SSG/SSR for public content, CSR or hybrid for authenticated apps with heavy client state.
What is the React Compiler (React Forget)?
The React Compiler (rolling out with React 19 ecosystem) automatically memoizes components and values when safe—reducing manual useMemo/useCallback/memo.
Interview angle: compiler does not remove need to understand rendering—it optimizes proven patterns.
Teams enable gradually with lint rules and compatibility checks.
A strong answer is:
The React Compiler auto-memoizes where safe—I still understand render flow because compiler coverage is not universal overnight.
Patterns, architecture, and coding scenarios
How do you avoid prop drilling?
Options:
| Approach | When |
|---|---|
| Composition | Pass children or render props |
| Context | Wide low-churn values |
| Colocation | Split components so intermediates disappear |
| Client store | Many distant consumers |
Do not reach for global state before trying composition and colocation.
A strong answer is:
I colocate state and compose children first; Context or a small store only when many distant components need the same client data.
What are React portals?
Portals render children into a different DOM node—typically document.body for modals and tooltips.
createPortal(<Modal />, document.getElementById("modal-root"));Event bubbling still follows React tree (not DOM tree)—important for delegation tests.
A strong answer is:
Portals render UI elsewhere in the DOM while keeping React event bubbling logical—essential for accessible modals and overlays.
Why must React state updates be immutable?
React compares references to detect changes. Mutating arrays/objects in place may skip re-render or cause subtle bugs.
// Wrong — mutates
state.push(item);
// Right — new reference
setState([...state, item]);
setState({ ...state, count: state.count + 1 });Immutability enables time-travel debugging and predictable memoization.
A strong answer is:
I always return new objects and arrays when updating state so React detects changes and memoization stays trustworthy.
How do synthetic events work in React?
React wraps browser events in SyntheticEvent for cross-browser normalization and event pooling (legacy optimization mostly irrelevant now).
| Detail | Interview note |
|---|---|
| Delegation | React 17+ attaches to root container |
preventDefault |
Still explicit for forms/links |
| Async access | event.persist() rarely needed in modern React |
Prefer controlled patterns over reading DOM after async without storing values.
A strong answer is:
Synthetic events normalize browser differences—I use controlled components and read values synchronously or from state, not stale events after await.
React coding interview scenarios
Coding scenario: Build a todo list — what do interviewers evaluate?
Expected features: add, toggle complete, filter (all/active/done), delete.
| Criteria | Strong signal |
|---|---|
| State shape | Array of { id, text, done } not parallel arrays |
| Keys | Stable id, not index |
| Immutability | Map/filter for updates |
| Accessibility | Labels, button types, list semantics |
| Edge cases | Empty state, duplicate submit |
function todosReducer(state, action) {
switch (action.type) {
case "add":
return [...state, { id: crypto.randomUUID(), text: action.text, done: false }];
case "toggle":
return state.map((t) => (t.id === action.id ? { ...t, done: !t.done } : t));
default:
return state;
}
}Mention useReducer when transitions multiply—shows structure for growing state machines.
A strong answer is:
I model todos as an immutable array with stable ids, use reducer or useState clearly, filter without mutating, and wire accessible controls—not a quick DOM hack.
Coding scenario: Search with API — debounce, abort, and loading states.
Checklist:
- Controlled input
- Debounce query (300–400 ms)
- AbortController cancel stale requests
- Loading / error / empty UI states
- Do not set state after unmount
useEffect(() => {
if (!debouncedQuery) return;
const controller = new AbortController();
setLoading(true);
fetch(`/api/search?q=${debouncedQuery}`, { signal: controller.signal })
.then((r) => r.json())
.then(setResults)
.catch((e) => { if (e.name !== "AbortError") setError(e); })
.finally(() => setLoading(false));
return () => controller.abort();
}, [debouncedQuery]);Senior mention: TanStack Query replaces most of this boilerplate with keepPreviousData and built-in cancellation patterns.
A strong answer is:
I debounce input, abort in-flight fetches on change, and render loading and error explicitly—or use TanStack Query if the stack allows.
Coding scenario: Fix a component that loops infinitely — common causes?
| Cause | Symptom |
|---|---|
useEffect sets state without proper deps |
Infinite loop |
useEffect(() => setX(...), [obj]) new object every render |
Loop |
| Fetch in effect missing stable deps | Repeated fetch storm |
Fix patterns:
- Narrow dependencies to primitives
- Functional updates:
setItems((prev) => ...) - Move object creation outside effect or memoize
- Do not derive state from state in effect when render can compute it
A strong answer is:
Infinite loops usually mean an effect writes state that retriggers itself—I fix dependency arrays, derive values in render instead of effects, and use functional updates.
Senior prep and final checklist
Scenario: Users report a slow React dashboard — how do you investigate?
| Step | Action |
|---|---|
| 1 | Reproduce with Profiler—which components render often? |
| 2 | Check network waterfall—overfetching APIs? |
| 3 | Large lists without virtualization? |
| 4 | Context or parent state causing wide re-renders? |
| 5 | Bundle size—missing code split? |
| 6 | Fix highest impact first; measure Core Web Vitals |
Connect to front end performance topics (LCP, INP).
A strong answer is:
I profile renders and network, fix the biggest measurable bottleneck—usually data fetching or list rendering—and verify with metrics before micro-optimizing hooks.
What should you rehearse the week before a React interview?
Checklist:
- Explain Virtual DOM, keys, and one-way data flow
-
useEffectdeps, cleanup, Strict Mode double invoke -
useMemo/useCallbackonly with justification - Server vs Client Components and when you would use each
- TanStack Query vs raw fetch
- Timed todo and debounced search coding reps
- Error boundaries and testing with RTL
- One performance and one production bug STAR story
- Front end fundamentals refresh
- Full stack integration if role is end-to-end
A strong answer is:
I rehearse hooks and coding exercises until they are muscle memory, then practice narrating trade-offs for data fetching and rendering like I would in a senior design review.
Pattern cheat sheet (quick reference)
| Need | React starting point |
|---|---|
| Local UI state | useState / useReducer |
| Side effects / subscriptions | useEffect + cleanup |
| Server data | TanStack Query / RSC |
| Avoid list bugs | Stable key={item.id} |
| Expensive child renders | memo + stable props (after profile) |
| Slow typing on filter | useTransition |
| Heavy route | lazy + Suspense |
| Forms (React 19) | Actions + useActionState |
| Global theme/auth | Context (low churn) |
| Modal overlay | Portal + focus trap |
| Tests | Testing Library + MSW |
References
Official React documentation
On-site prep
- Front end developer interview questions
- Full stack developer interviews
- Angular developer interviews
- Node.js developer interviews
- Spring Boot interview questions
- Python developer interviews
- Git interview questions
- Interview Questions category
Summary
React interviews test hooks as synchronization, smart state and data-fetching choices, and calm debugging under time pressure—not Virtual DOM definitions. Answer aloud, code timed exercises, and compare your structure to each section. Pair with front end and full stack prep when the role spans the whole feature.

