React interview questions in 2026 cover hooks, state management, performance optimization, React 19’s new features, and TypeScript patterns. This guide covers the most commonly asked React questions at both junior and senior levels.
📋 Table of Contents
Core React Questions
1. What is the virtual DOM and how does reconciliation work?
The virtual DOM is a lightweight JavaScript representation of the real DOM. When state changes:
- React creates a new virtual DOM tree
- Diffing algorithm compares new vs previous virtual DOM
- Only changed nodes are updated in the real DOM
- This batching of updates makes React performant
React 18+ uses concurrent rendering — can pause, interrupt, and resume rendering, prioritizing urgent updates.
2. Explain useEffect and its common pitfalls
import { useEffect, useState } from 'react';
function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState<User | null>(null);
// Correct: cleanup function + proper dependencies
useEffect(() => {
let cancelled = false; // prevent stale closure
async function fetchUser() {
const data = await api.getUser(userId);
if (!cancelled) setUser(data); // only update if still mounted
}
fetchUser();
return () => { cancelled = true; }; // cleanup
}, [userId]); // dependency array
// PITFALL 1: missing dependency
// useEffect(() => { fetchUser(userId); }, []);
// This won't re-run when userId changes!
// PITFALL 2: object/array in deps (new reference every render)
// useEffect(() => { ... }, [options]); // infinite loop if options = {}
// Fix: useMemo(() => options, [dep1, dep2])
// PITFALL 3: no cleanup for subscriptions
useEffect(() => {
const sub = websocket.subscribe(userId, setUser);
return () => sub.unsubscribe(); // ALWAYS cleanup subscriptions!
}, [userId]);
return user ? <Profile user={user} /> : <Loading />;
}
3. When should you use useMemo and useCallback?
// useMemo — memoize expensive computed value
const filteredUsers = useMemo(
() => users.filter(u => u.active && u.role === selectedRole),
[users, selectedRole] // recompute only when these change
);
// useCallback — memoize function reference
const handleDelete = useCallback(
(userId: number) => {
setUsers(prev => prev.filter(u => u.id !== userId));
},
[] // no deps — stable reference
);
// Use useMemo when:
// - Expensive computation (sorting, filtering large arrays)
// - Object/array passed to children with React.memo
// - Dependency for other hooks
// Use useCallback when:
// - Passing to child component wrapped in React.memo
// - As dependency in useEffect to prevent re-runs
// DON'T overuse — memoization has overhead!
// Bad: const value = useMemo(() => a + b, [a, b]); // trivial, skip it
4. What is React.memo and when to use it?
// React.memo — prevent re-render if props haven't changed
const ExpensiveList = React.memo(function ExpensiveList({ items, onItemClick }: Props) {
console.log('ExpensiveList rendered');
return (
<ul>
{items.map(item => (
<li key={item.id} onClick={() => onItemClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
});
// Parent using it correctly
function Parent() {
const [count, setCount] = useState(0);
const [items] = useState(bigItemList);
// useCallback ensures stable reference (required for memo to work)
const handleClick = useCallback((id: number) => {
console.log('clicked', id);
}, []);
return (
<div>
<button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
{/* ExpensiveList won't re-render when count changes */}
<ExpensiveList items={items} onItemClick={handleClick} />
</div>
);
}
State Management
5. When should you use Redux vs Zustand vs Context API?
| Tool | Use When | Avoid When |
|---|---|---|
| Context API | Theme, locale, auth state (infrequent updates) | Frequent updates (causes re-renders) |
| Zustand | Most apps needing global state | Very complex event sourcing needs |
| Redux Toolkit | Large teams, complex state, time-travel debugging | Simple apps (overkill) |
| TanStack Query | Server state (always) | Not for UI state |
React 19 Questions
6. What are Server Components and how are they different from Client Components?
// Server Component (default in Next.js App Router)
// - Runs only on server, never on client
// - Can access databases, file system directly
// - Zero JS sent to client (except HTML)
// - Cannot use state, effects, event handlers
async function ProductList() { // no 'use client'
const products = await db.products.findMany(); // direct DB access!
return (
<ul>
{products.map(p => <ProductCard key={p.id} product={p} />)}
</ul>
);
}
// Client Component (must add 'use client')
// - Runs on both server (initial HTML) and client (hydration)
// - Can use useState, useEffect, event handlers
// - Receives props from Server Components
'use client';
function AddToCart({ productId }: { productId: number }) {
const [added, setAdded] = useState(false);
return (
<button onClick={() => {
addToCart(productId);
setAdded(true);
}}>
{added ? 'Added!' : 'Add to Cart'}
</button>
);
}
7. What are React 19’s new features?
- Server Actions — async functions that run on server, callable from client
- use() hook — read promises and context in render (replaces some useEffect patterns)
- Form actions — native form handling with server functions
- Optimistic updates — useOptimistic() for immediate UI feedback
- Document metadata — title, meta tags in components directly
- Asset loading — Suspense integration for images and CSS
8. How do you handle errors in React?
// Error Boundary (class component, required for now)
class ErrorBoundary extends React.Component<
{ fallback: React.ReactNode; children: React.ReactNode },
{ hasError: boolean }
> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error, info: React.ErrorInfo) {
logErrorToService(error, info);
}
render() {
if (this.state.hasError) return this.props.fallback;
return this.props.children;
}
}
// Usage
<ErrorBoundary fallback={<ErrorPage />}>
<UserProfile />
</ErrorBoundary>
// React 19: useErrorBoundary hook coming (use 'react-error-boundary' lib now)
React interview prep: understand hooks deeply (especially useEffect dependencies), know when to optimize (React.memo + useCallback/useMemo), explain the server/client component split, and know current state management options. Build a few production apps to understand these concepts experientially, not just theoretically.
📚 You might also like
🔗 Share this article




✍️ Leave a Comment