Top 40 React.js Interview Questions & Answers (2026)
Top 40 React.js Interview Questions & Answers (2026)
Last Updated: March 2026 | Level: Beginner to Advanced | Format: Q&A with Code Snippets
React.js continues to dominate frontend development in 2026. Whether you're a fresher or a senior engineer, these 40 curated questions cover everything interviewers ask — from JSX fundamentals to advanced performance optimization patterns.
Table of Contents
- JSX & Components (Q1–Q10)
- Hooks (Q11–Q20)
- State Management (Q21–Q28)
- Performance Optimization (Q29–Q34)
- Patterns & Architecture (Q35–Q40)
JSX & Components
Q1. What is JSX and how does it differ from HTML? Easy
Key differences from HTML:
- Use
classNameinstead ofclass - Use
htmlForinstead offor - Self-closing tags are mandatory:
<img />,<br /> - JavaScript expressions go inside
{}curly braces styletakes an object, not a string:style={{ color: 'red' }}
// JSX
const element = (
<div className="container" style={{ padding: '10px' }}>
<h1>Hello, {name}!</h1>
<img src={logo} alt="Logo" />
</div>
);
// Transpiled to:
const element = React.createElement(
'div',
{ className: 'container', style: { padding: '10px' } },
React.createElement('h1', null, 'Hello, ', name, '!'),
React.createElement('img', { src: logo, alt: 'Logo' })
);
Q2. What is the difference between a functional component and a class component? Easy
| Feature | Functional Component | Class Component |
|---|---|---|
| Syntax | JavaScript function | ES6 class extending React.Component |
| State | via useState hook | this.state |
| Lifecycle | via useEffect | lifecycle methods |
this keyword | Not needed | Required |
| Performance | Slightly faster | More overhead |
| Current recommendation | ✅ Preferred | Legacy |
// Functional Component (Modern)
function Greeting({ name }) {
const [count, setCount] = useState(0);
return <h1>Hello, {name}! Clicked {count} times.</h1>;
}
// Class Component (Legacy)
class Greeting extends React.Component {
state = { count: 0 };
render() {
return <h1>Hello, {this.props.name}! Clicked {this.state.count} times.</h1>;
}
}
Q3. What are props in React? Can you modify props inside a component? Easy
No, you cannot modify props inside a component. Props are immutable — treating them as read-only is a core React principle. If you need to modify data, lift state up or use local state.
// Parent passes props
function App() {
return <UserCard name="Aditya" role="Developer" age={25} />;
}
// Child receives and uses props (read-only)
function UserCard({ name, role, age }) {
// ❌ props.name = "Someone"; // This would cause an error
return (
<div>
<h2>{name}</h2>
<p>{role} · {age} years</p>
</div>
);
}
Q4. What is the Virtual DOM and how does React's reconciliation algorithm work? Medium
- Creates a new Virtual DOM tree
- Diffs it against the previous VDOM (using the Diffing Algorithm)
- Computes the minimal set of changes needed
- Applies only those changes to the real DOM (reconciliation)
React's diffing algorithm uses two heuristics:
- Same type elements → update in place (keep DOM node, update attributes)
- Different type elements → destroy old subtree, build new one
- Lists → use
keyprop for efficient reordering
// Without keys — React can't efficiently reconcile list reorders
<ul>
{items.map(item => <li>{item.name}</li>)} // ❌ No key
// With keys — React tracks items correctly
{items.map(item => <li key={item.id}>{item.name}</li>)} // ✅
</ul>
Q5. What are React Fragments and why use them? Easy
// Without Fragment — adds an extra <div>
function TableRow() {
return (
<div> {/* ❌ Invalid inside <table> */}
<td>Name</td>
<td>Age</td>
</div>
);
}
// With Fragment — no extra DOM node
function TableRow() {
return (
<>
<td>Name</td>
<td>Age</td>
</>
);
}
// Long syntax (supports key prop)
function List({ items }) {
return items.map(item => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.desc}</dd>
</React.Fragment>
));
}
Q6. What are controlled vs uncontrolled components? Medium
Controlled Component: Form data is managed by React state. The component "controls" the input value.
Uncontrolled Component: Form data is managed by the DOM itself. React accesses it via refs.
// Controlled Component
function ControlledForm() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}
// Uncontrolled Component
function UncontrolledForm() {
const inputRef = useRef(null);
const handleSubmit = () => console.log(inputRef.current.value);
return (
<>
<input ref={inputRef} defaultValue="initial" />
<button onClick={handleSubmit}>Submit</button>
</>
);
}
Use controlled for: Validation, conditional rendering, dynamic forms.
Use uncontrolled for: File inputs, integrating with non-React libraries.
Q7. What is prop drilling and how can you avoid it? Medium
// Problem: Prop drilling
function App() {
const user = { name: 'Aditya' };
return <Parent user={user} />;
}
function Parent({ user }) {
return <Child user={user} />; // Parent doesn't need user
}
function Child({ user }) {
return <GrandChild user={user} />; // Child doesn't need user
}
function GrandChild({ user }) {
return <h1>Hello, {user.name}</h1>; // Finally uses it
}
// Solution 1: React Context
const UserContext = createContext();
function App() {
return (
<UserContext.Provider value={{ name: 'Aditya' }}>
<Parent />
</UserContext.Provider>
);
}
function GrandChild() {
const user = useContext(UserContext);
return <h1>Hello, {user.name}</h1>;
}
// Solution 2: State Management (Redux, Zustand, Jotai)
Q8. What is key prop in React and why is it important? Easy
Rules for keys:
- Must be stable (don't use Math.random() or array index if list can reorder)
- Must be unique among siblings (not globally)
- Prefer database IDs or unique strings
// ❌ Bad: Using index as key (breaks on reorder/delete)
{items.map((item, index) => <li key={index}>{item.name}</li>)}
// ✅ Good: Using stable unique ID
{items.map(item => <li key={item.id}>{item.name}</li>)}
Q9. What is the difference between defaultProps and default parameter values? Medium
// defaultProps (class component era, still works)
function Button({ label, color }) {
return <button style={{ background: color }}>{label}</button>;
}
Button.defaultProps = {
label: 'Click me',
color: 'blue'
};
// Default parameter values (modern, preferred)
function Button({ label = 'Click me', color = 'blue' }) {
return <button style={{ background: color }}>{label}</button>;
}
Default parameter values are preferred in modern React because:
- Works with TypeScript type inference out of the box
- No separate
defaultPropsobject to maintain defaultPropsis deprecated for function components in React 18.3+
Q10. What are Higher-Order Components (HOC)? Hard
// HOC for adding authentication guard
function withAuth(WrappedComponent) {
return function AuthenticatedComponent(props) {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <Redirect to="/login" />;
}
return <WrappedComponent {...props} />;
};
}
// HOC for adding loading state
function withLoading(WrappedComponent) {
return function WithLoadingComponent({ isLoading, ...props }) {
if (isLoading) return <Spinner />;
return <WrappedComponent {...props} />;
};
}
// Usage
const ProtectedDashboard = withAuth(withLoading(Dashboard));
When to use HOCs: Cross-cutting concerns like auth, logging, theming. Modern alternatives include custom hooks and render props.
Hooks
Q11. What are React Hooks? Why were they introduced? Easy
Problems they solved:
- Reusing stateful logic between components required HOCs/render props (complex)
- Complex components became hard to understand when lifecycle logic was split
- Classes confused both humans and machines (minification issues,
thisbinding)
Rules of Hooks:
- Only call hooks at the top level (not inside loops, conditions, or nested functions)
- Only call hooks from React function components or custom hooks
// Built-in hooks
useState, useEffect, useContext, useReducer,
useCallback, useMemo, useRef, useLayoutEffect,
useImperativeHandle, useDebugValue,
// React 18+
useId, useTransition, useDeferredValue, useSyncExternalStore,
// React 19+
useActionState, useFormStatus, useOptimistic
Q12. Explain useState with examples of common pitfalls. Easy
const [state, setState] = useState(initialValue);
// Pitfall 1: State updates are asynchronous
function Counter() {
const [count, setCount] = useState(0);
// ❌ Stale closure — both use same `count`
const handleBadClick = () => {
setCount(count + 1);
setCount(count + 1); // Still increments by 1, not 2
};
// ✅ Functional update — always uses latest state
const handleGoodClick = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1); // Correctly increments by 2
};
return <button onClick={handleGoodClick}>{count}</button>;
}
// Pitfall 2: Objects — must spread old state
const [user, setUser] = useState({ name: 'Aditya', age: 25 });
// ❌ Replaces entire object
setUser({ name: 'Raj' }); // age is lost!
// ✅ Spread existing state
setUser(prev => ({ ...prev, name: 'Raj' }));
Q13. How does useEffect work? Explain cleanup and dependency array. Medium
useEffect(() => {
// Side effect code
return () => {
// Cleanup (runs before next effect or on unmount)
};
}, [dependencies]);
Dependency array behaviors:
[]— Runs once on mount (componentDidMount equivalent)[dep1, dep2]— Runs when dep1 or dep2 changes- Omitted — Runs after every render
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchUser() {
const data = await api.getUser(userId);
if (!cancelled) setUser(data);
}
fetchUser();
// Cleanup: prevent state update on unmounted component
return () => { cancelled = true; };
}, [userId]); // Re-runs when userId changes
// Timer cleanup example
useEffect(() => {
const interval = setInterval(() => console.log('tick'), 1000);
return () => clearInterval(interval); // ✅ Always clean up timers
}, []);
}
Q14. What is useContext and when should you use it? Medium
// 1. Create context
const ThemeContext = createContext('light');
// 2. Provide value at top level
function App() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<MainLayout />
</ThemeContext.Provider>
);
}
// 3. Consume anywhere in the tree
function Button() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
className={`btn-${theme}`}
onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}
>
Toggle Theme
</button>
);
}
Use context for: Theme, locale, auth user, feature flags.
Avoid for: High-frequency updates (use Redux/Zustand instead — context triggers re-render of ALL consumers).
Q15. Explain useReducer and when to prefer it over useState. Medium
// Reducer pattern
const initialState = { count: 0, step: 1 };
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + state.step };
case 'DECREMENT':
return { ...state, count: state.count - state.step };
case 'SET_STEP':
return { ...state, step: action.payload };
case 'RESET':
return initialState;
default:
throw new Error(`Unknown action: ${action.type}`);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count} | Step: {state.step}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
<button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
</div>
);
}
Prefer useReducer when:
- State has multiple sub-values that change together
- Next state depends on previous state in complex ways
- Logic is complex enough to benefit from testing the reducer separately
Q16. What is useCallback and how does it prevent unnecessary re-renders? Hard
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// ❌ New function reference on every render
const handleClick = () => console.log('clicked', count);
// ✅ Stable reference, only changes when `count` changes
const handleClick = useCallback(() => {
console.log('clicked', count);
}, [count]);
return (
<>
<input onChange={e => setText(e.target.value)} value={text} />
{/* Child only re-renders when handleClick reference changes */}
<MemoizedChild onClick={handleClick} />
</>
);
}
// Child must be wrapped in React.memo to benefit
const MemoizedChild = React.memo(({ onClick }) => {
console.log('Child rendered');
return <button onClick={onClick}>Click</button>;
});
Important: useCallback only makes sense paired with React.memo or when passing to deps arrays of other hooks.
Q17. What is useMemo and how does it differ from useCallback? Hard
useMemo— memoizes the return value of a function (expensive computation)useCallback— memoizes the function itself (stable reference)
// useMemo: memoize expensive calculation result
function ProductList({ products, filterText }) {
const filteredProducts = useMemo(() => {
console.log('Filtering products...'); // Only logs when deps change
return products
.filter(p => p.name.toLowerCase().includes(filterText.toLowerCase()))
.sort((a, b) => a.price - b.price);
}, [products, filterText]);
return filteredProducts.map(p => <ProductCard key={p.id} product={p} />);
}
// useCallback: memoize function reference
const handleDelete = useCallback((id) => {
setProducts(prev => prev.filter(p => p.id !== id));
}, []); // Empty array: function never changes
// useMemo equivalence to useCallback
// useCallback(fn, deps) === useMemo(() => fn, deps)
Q18. What is useRef and what are its use cases? Medium
// Use case 1: Accessing DOM elements
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => inputRef.current.focus();
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus</button>
</>
);
}
// Use case 2: Storing mutable values that don't trigger re-render
function Timer() {
const [seconds, setSeconds] = useState(0);
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
};
const stop = () => clearInterval(intervalRef.current);
return (
<div>
<p>{seconds}s</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}
// Use case 3: Tracking previous value
function usePrevious(value) {
const ref = useRef();
useEffect(() => { ref.current = value; });
return ref.current; // Previous value
}
Q19. What are custom hooks and how do you create one? Medium
// Custom hook: useFetch
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const abortController = new AbortController();
async function fetchData() {
try {
setLoading(true);
const res = await fetch(url, { signal: abortController.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
setData(json);
} catch (err) {
if (err.name !== 'AbortError') setError(err.message);
} finally {
setLoading(false);
}
}
fetchData();
return () => abortController.abort();
}, [url]);
return { data, loading, error };
}
// Usage — clean and reusable
function UserProfile({ id }) {
const { data: user, loading, error } = useFetch(`/api/users/${id}`);
if (loading) return <Spinner />;
if (error) return <Error message={error} />;
return <div>{user.name}</div>;
}
Q20. What is useLayoutEffect and how does it differ from useEffect? Hard
useEffect | useLayoutEffect | |
|---|---|---|
| Timing | After paint (async) | After DOM mutations, before paint (sync) |
| Blocks paint | No | Yes |
| Use case | Data fetching, subscriptions | DOM measurements, animations |
| SSR | Safe | Causes warning (no DOM on server) |
// useLayoutEffect: measure DOM before browser paints
function Tooltip({ children, text }) {
const [coords, setCoords] = useState({ top: 0, left: 0 });
const tooltipRef = useRef(null);
useLayoutEffect(() => {
// This runs synchronously after DOM update, before paint
// Prevents flickering you'd see with useEffect
const rect = tooltipRef.current.getBoundingClientRect();
setCoords({
top: rect.bottom + window.scrollY,
left: rect.left + window.scrollX
});
}, []);
return (
<>
<span ref={tooltipRef}>{children}</span>
<div style={coords}>{text}</div>
</>
);
}
State Management
Q21. What is the Context API and what are its limitations? Medium
Limitations:
- Performance: All consumers re-render when context value changes (even if they only use part of it)
- Debugging: Context changes are harder to track than Redux actions
- Multiple contexts: Can lead to "context hell" (deeply nested providers)
// Performance issue — ALL consumers re-render
const AppContext = createContext();
// ❌ Object literal creates new reference every render
<AppContext.Provider value={{ user, theme, setTheme }}>
// ✅ Memoize context value
const contextValue = useMemo(() => ({ user, theme, setTheme }), [user, theme]);
<AppContext.Provider value={contextValue}>
// ✅ Split contexts by update frequency
<UserContext.Provider value={user}>
<ThemeContext.Provider value={{ theme, setTheme }}>
<App />
</ThemeContext.Provider>
</UserContext.Provider>
Q22. What is Redux and explain its core principles? Medium
- Single source of truth: All app state lives in one store
- State is read-only: Only dispatching actions can change state
- Changes made with pure functions: Reducers are pure functions
Action → Reducer → Store → View → (User interaction) → Action
// Redux Toolkit (modern Redux)
import { createSlice, configureStore } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: state => { state.value += 1; }, // Immer handles immutability
decrement: state => { state.value -= 1; },
incrementByAmount: (state, action) => { state.value += action.payload; },
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
const store = configureStore({ reducer: { counter: counterSlice.reducer } });
// Component
function Counter() {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();
return (
<button onClick={() => dispatch(increment())}>{count}</button>
);
}
Q23. What is Zustand and how does it compare to Redux? Medium
// Zustand store — much simpler than Redux
import { create } from 'zustand';
const useStore = create((set, get) => ({
bears: 0,
user: null,
// Actions directly in store
addBear: () => set(state => ({ bears: state.bears + 1 })),
setUser: (user) => set({ user }),
// Derived state
getBearCount: () => get().bears,
}));
// Component — no Provider needed!
function BearCounter() {
const { bears, addBear } = useStore();
return <button onClick={addBear}>Bears: {bears}</button>;
}
| Redux Toolkit | Zustand | |
|---|---|---|
| Boilerplate | Medium | Minimal |
| Bundle size | ~15kb | ~1kb |
| DevTools | Excellent | Good |
| Learning curve | Steeper | Easy |
| Best for | Large teams, complex apps | Medium apps, quick setup |
Q24. What is React Query (TanStack Query) and what problem does it solve? Hard
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// Fetching data
function UserList() {
const { data, isLoading, error, isStale } = useQuery({
queryKey: ['users'], // Cache key
queryFn: () => api.getUsers(), // Fetch function
staleTime: 5 * 60 * 1000, // 5 minutes fresh
gcTime: 10 * 60 * 1000, // 10 minutes cache
});
if (isLoading) return <Spinner />;
if (error) return <Error />;
return data.map(user => <UserCard key={user.id} user={user} />);
}
// Mutations with cache invalidation
function AddUser() {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: api.createUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] }); // Refetch list
},
});
return <button onClick={() => mutation.mutate({ name: 'New User' })}>Add</button>;
}
Problems it solves: Caching, background refetching, deduplication, pagination, optimistic updates, loading/error states — all without Redux boilerplate.
Q25. What is Jotai and the atomic state management pattern? Hard
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
// Define atoms
const countAtom = atom(0);
const doubledAtom = atom(get => get(countAtom) * 2); // Derived atom
// Async atom
const userAtom = atom(async () => {
const res = await fetch('/api/user');
return res.json();
});
function Counter() {
const [count, setCount] = useAtom(countAtom);
const doubled = useAtomValue(doubledAtom); // Read-only
return (
<div>
<p>Count: {count}, Doubled: {doubled}</p>
<button onClick={() => setCount(c => c + 1)}>+</button>
</div>
);
}
Advantage: Only components that subscribe to a specific atom re-render — fine-grained reactivity without selectors.
Q26. How does React's useTransition hook help with state updates? Hard
function SearchPage() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleSearch = (e) => {
setQuery(e.target.value); // Urgent — update input immediately
startTransition(() => {
// Non-urgent — can be interrupted by other updates
setResults(searchDatabase(e.target.value)); // Expensive operation
});
};
return (
<>
<input value={query} onChange={handleSearch} />
{isPending ? (
<p>Loading results...</p>
) : (
<ResultsList results={results} />
)}
</>
);
}
Q27. What are the differences between useTransition and useDeferredValue? Hard
useTransition | useDeferredValue | |
|---|---|---|
| What you control | State update function | A value |
| Use when | You own the state setter | You receive value from parent |
| Returns | [isPending, startTransition] | deferred value |
// useTransition — when you control the state
const [isPending, startTransition] = useTransition();
startTransition(() => setResults(compute(query)));
// useDeferredValue — when you receive a prop/value
function ResultsList({ query }) {
const deferredQuery = useDeferredValue(query);
// Uses stale query until React has time to update
const results = useMemo(() => searchDatabase(deferredQuery), [deferredQuery]);
return <List items={results} />;
}
Q28. What is useOptimistic (React 19)? Hard
function TodoList({ todos, addTodo }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo) => [...state, { ...newTodo, pending: true }]
);
async function handleSubmit(formData) {
const newTodo = { text: formData.get('todo'), id: Date.now() };
addOptimisticTodo(newTodo); // Immediately shows in UI
await addTodo(newTodo); // Actual async operation
// On success: server state replaces optimistic state
// On error: reverts to previous state
}
return (
<form action={handleSubmit}>
{optimisticTodos.map(todo => (
<li key={todo.id} style={{ opacity: todo.pending ? 0.5 : 1 }}>
{todo.text}
</li>
))}
<input name="todo" />
<button type="submit">Add</button>
</form>
);
}
Performance Optimization
Q29. What is React.memo and when should you use it? Medium
// Without memo — re-renders every time parent renders
function ExpensiveList({ items }) {
console.log('ExpensiveList rendered');
return items.map(item => <div key={item.id}>{item.name}</div>);
}
// With memo — only re-renders when items prop changes
const ExpensiveList = React.memo(({ items }) => {
return items.map(item => <div key={item.id}>{item.name}</div>);
});
// Custom comparison function
const arePropsEqual = (prevProps, nextProps) => {
return prevProps.items.length === nextProps.items.length;
};
const ExpensiveList = React.memo(({ items }) => { ... }, arePropsEqual);
Use React.memo when:
- Component renders often with the same props
- Component is computationally expensive to render
- Component receives simple, primitive props (easier equality check)
Don't use when: Component almost always receives new props — the comparison overhead is wasted.
Q30. What is code splitting and how do you implement it in React? Medium
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
// Lazy load heavy components
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Reports = lazy(() => import('./pages/Reports'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/reports" element={<Reports />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
// Named exports require explicit import
const MyChart = lazy(() =>
import('./components/Charts').then(module => ({ default: module.BarChart }))
);
Q31. What is React's Concurrent Mode and how does it change rendering? Hard
Key concepts:
- Interruptible rendering: React can pause and resume renders
- Priority scheduling: Urgent updates (typing) interrupt less urgent ones (search results)
- Automatic batching: Multiple state updates in any context are batched automatically
// React 18 — createRoot enables all concurrent features
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
// Automatic batching (React 18)
// ❌ React 17: caused 2 re-renders
// ✅ React 18: batched into 1 re-render
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// Only ONE re-render in React 18!
}, 100);
Q32. How do you optimize a React list with thousands of items? Hard
// Using @tanstack/react-virtual (TanStack Virtual)
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const parentRef = useRef(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50, // Estimated row height
overscan: 5, // Extra items rendered outside viewport
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
{virtualizer.getVirtualItems().map(virtualItem => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
transform: `translateY(${virtualItem.start}px)`,
width: '100%',
height: `${virtualItem.size}px`,
}}
>
{items[virtualItem.index].name}
</div>
))}
</div>
</div>
);
}
Q33. What are React Server Components (RSC) and what problems do they solve? Hard
Problems they solve:
- Large client bundles (component JS never ships to client)
- Waterfall fetches (server has direct database access)
- Security (sensitive data/keys stay on server)
// app/users/page.tsx (Next.js 14 — Server Component by default)
async function UsersPage() {
// Direct database query — no API route needed!
const users = await db.query('SELECT * FROM users');
return (
<div>
<h1>Users</h1>
{/* Data fetched, rendered on server — zero JS sent to client */}
{users.map(user => <UserCard key={user.id} user={user} />)}
{/* Add "use client" for interactive parts */}
<AddUserButton /> {/* This is a Client Component */}
</div>
);
}
// 'use client' — AddUserButton.tsx
'use client';
function AddUserButton() {
const [open, setOpen] = useState(false);
return <button onClick={() => setOpen(true)}>Add User</button>;
}
Q34. What are React Compiler (React Forget) and its implications? Hard
// Before React Compiler — manual memoization
function Component({ items, onDelete }) {
const filtered = useMemo(
() => items.filter(i => i.active),
[items]
);
const handleDelete = useCallback(
(id) => onDelete(id),
[onDelete]
);
return <MemoizedList items={filtered} onDelete={handleDelete} />;
}
// After React Compiler — compiler handles it automatically
function Component({ items, onDelete }) {
const filtered = items.filter(i => i.active); // Compiler memoizes this
return <List items={filtered} onDelete={onDelete} />; // Compiler optimizes
}
The React Compiler was released as stable in React 19 and is opt-in. It enforces the Rules of React as a prerequisite.
Patterns & Architecture
Q35. What is the Render Props pattern? Hard
// Mouse tracker using render props
class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (e) => {
this.setState({ x: e.clientX, y: e.clientY });
};
render() {
return (
<div onMouseMove={this.handleMouseMove}>
{/* Call the render prop function */}
{this.props.render(this.state)}
</div>
);
}
}
// Usage
<MouseTracker render={({ x, y }) => (
<h1>Mouse at: {x}, {y}</h1>
)} />
// Modern equivalent: custom hook
function useMousePosition() {
const [pos, setPos] = useState({ x: 0, y: 0 });
useEffect(() => {
const handler = e => setPos({ x: e.clientX, y: e.clientY });
window.addEventListener('mousemove', handler);
return () => window.removeEventListener('mousemove', handler);
}, []);
return pos;
}
Q36. What is the Compound Component pattern? Hard
const TabsContext = createContext();
function Tabs({ children, defaultTab }) {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
Tabs.List = function TabList({ children }) {
return <div className="tab-list">{children}</div>;
};
Tabs.Tab = function Tab({ id, children }) {
const { activeTab, setActiveTab } = useContext(TabsContext);
return (
<button
className={activeTab === id ? 'active' : ''}
onClick={() => setActiveTab(id)}
>
{children}
</button>
);
};
Tabs.Panel = function Panel({ id, children }) {
const { activeTab } = useContext(TabsContext);
return activeTab === id ? <div>{children}</div> : null;
};
// Usage — clean, readable API
<Tabs defaultTab="profile">
<Tabs.List>
<Tabs.Tab id="profile">Profile</Tabs.Tab>
<Tabs.Tab id="settings">Settings</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="profile"><ProfileContent /></Tabs.Panel>
<Tabs.Panel id="settings"><SettingsContent /></Tabs.Panel>
</Tabs>
Q37. What is Error Boundary in React? Medium
// Error boundaries must be class components (no hook equivalent)
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, info) {
logErrorToService(error, info.componentStack);
}
render() {
if (this.state.hasError) {
return this.props.fallback || <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary fallback={<ErrorPage />}>
<UserDashboard />
</ErrorBoundary>
// React 19 adds: use errorBoundary prop in Suspense-like API
// Libraries: react-error-boundary (recommended)
import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary FallbackComponent={ErrorFallback} onError={logError}>
<App />
</ErrorBoundary>
Q38. What is the difference between SSR, SSG, and ISR in React? Hard
| SSR | SSG | ISR | |
|---|---|---|---|
| Full form | Server-Side Rendering | Static Site Generation | Incremental Static Regeneration |
| When HTML generated | Each request | Build time | Build time + revalidation |
| Data freshness | Always fresh | Stale (until rebuild) | Stale-while-revalidate |
| Performance | Slower TTFB | Fastest | Fast |
| Best for | Personalized, dynamic | Blogs, docs | E-commerce, news |
// Next.js App Router examples
// SSR — runs on every request
async function Page() {
const data = await fetch(url, { cache: 'no-store' });
return <Content data={data} />;
}
// SSG — runs at build time
async function Page() {
const data = await fetch(url, { cache: 'force-cache' });
return <Content data={data} />;
}
// ISR — revalidates every 60 seconds
async function Page() {
const data = await fetch(url, { next: { revalidate: 60 } });
return <Content data={data} />;
}
Q39. What are React portals and when do you use them? Medium
import { createPortal } from 'react-dom';
function Modal({ isOpen, onClose, children }) {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay">
<div className="modal-content">
{children}
<button onClick={onClose}>Close</button>
</div>
</div>,
document.getElementById('modal-root') // Rendered here, not in parent
);
}
// Even though rendered outside, React events still bubble normally
function App() {
const [open, setOpen] = useState(false);
return (
<div onClick={() => console.log('App clicked')}> {/* This fires! */}
<button onClick={() => setOpen(true)}>Open Modal</button>
<Modal isOpen={open} onClose={() => setOpen(false)}>
<p>Modal content</p>
</Modal>
</div>
);
}
Use cases: Modals, tooltips, dropdowns, toasts — anything that needs to break out of overflow/z-index constraints.
Q40. Explain React's new use hook (React 19). Hard
import { use, Suspense } from 'react';
// Reading a Promise
function UserProfile({ userPromise }) {
const user = use(userPromise); // Suspends if not ready
return <div>{user.name}</div>;
}
function App() {
const userPromise = fetchUser(123); // Promise created outside component
return (
<Suspense fallback={<Spinner />}>
<UserProfile userPromise={userPromise} />
</Suspense>
);
}
// Reading Context conditionally (unlike useContext!)
function Component({ shouldGetTheme }) {
if (shouldGetTheme) {
const theme = use(ThemeContext); // ✅ Conditional hook call allowed
return <div style={{ color: theme.color }}>Content</div>;
}
return <div>No theme</div>;
}
Quick Reference: React Interview Cheat Sheet
| Topic | Key Concept |
|---|---|
| VDOM | Lightweight JS object tree, diff + reconcile |
| Hooks rules | Top level only, functional components only |
useState | Functional updates for state-dependent changes |
useEffect | Always cleanup; deps array controls timing |
useMemo | Memoize values; useCallback memoizes functions |
useRef | Mutable container; DOM access; no re-render |
| Context | Great for low-frequency global state |
| React.memo | Prevents re-render if props unchanged |
| Error Boundary | Class component; catches child errors |
| RSC | Server only; no hooks, no interactivity |
| Code Splitting | React.lazy + Suspense |
| Portals | Render outside parent DOM hierarchy |
Prepared for placements 2026 | PapersAdda.com
Explore this topic cluster
More resources in interview-questions
Use the category hub to browse similar questions, exam patterns, salary guides, and preparation resources related to this topic.