PapersAdda

Top 40 React.js Interview Questions & Answers (2026)

29 min read
interview-questions
Advertisement Placement

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

  1. JSX & Components (Q1–Q10)
  2. Hooks (Q11–Q20)
  3. State Management (Q21–Q28)
  4. Performance Optimization (Q29–Q34)
  5. Patterns & Architecture (Q35–Q40)

JSX & Components

Q1. What is JSX and how does it differ from HTML? Easy

Key differences from HTML:

  • Use className instead of class
  • Use htmlFor instead of for
  • Self-closing tags are mandatory: <img />, <br />
  • JavaScript expressions go inside {} curly braces
  • style takes 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

FeatureFunctional ComponentClass Component
SyntaxJavaScript functionES6 class extending React.Component
Statevia useState hookthis.state
Lifecyclevia useEffectlifecycle methods
this keywordNot neededRequired
PerformanceSlightly fasterMore overhead
Current recommendation✅ PreferredLegacy
// 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

  1. Creates a new Virtual DOM tree
  2. Diffs it against the previous VDOM (using the Diffing Algorithm)
  3. Computes the minimal set of changes needed
  4. 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 key prop 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 defaultProps object to maintain
  • defaultProps is 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, this binding)

Rules of Hooks:

  1. Only call hooks at the top level (not inside loops, conditions, or nested functions)
  2. 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

useEffectuseLayoutEffect
TimingAfter paint (async)After DOM mutations, before paint (sync)
Blocks paintNoYes
Use caseData fetching, subscriptionsDOM measurements, animations
SSRSafeCauses 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:

  1. Performance: All consumers re-render when context value changes (even if they only use part of it)
  2. Debugging: Context changes are harder to track than Redux actions
  3. 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

  1. Single source of truth: All app state lives in one store
  2. State is read-only: Only dispatching actions can change state
  3. 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 ToolkitZustand
BoilerplateMediumMinimal
Bundle size~15kb~1kb
DevToolsExcellentGood
Learning curveSteeperEasy
Best forLarge teams, complex appsMedium 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

useTransitionuseDeferredValue
What you controlState update functionA value
Use whenYou own the state setterYou 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

SSRSSGISR
Full formServer-Side RenderingStatic Site GenerationIncremental Static Regeneration
When HTML generatedEach requestBuild timeBuild time + revalidation
Data freshnessAlways freshStale (until rebuild)Stale-while-revalidate
PerformanceSlower TTFBFastestFast
Best forPersonalized, dynamicBlogs, docsE-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

TopicKey Concept
VDOMLightweight JS object tree, diff + reconcile
Hooks rulesTop level only, functional components only
useStateFunctional updates for state-dependent changes
useEffectAlways cleanup; deps array controls timing
useMemoMemoize values; useCallback memoizes functions
useRefMutable container; DOM access; no re-render
ContextGreat for low-frequency global state
React.memoPrevents re-render if props unchanged
Error BoundaryClass component; catches child errors
RSCServer only; no hooks, no interactivity
Code SplittingReact.lazy + Suspense
PortalsRender outside parent DOM hierarchy

Prepared for placements 2026 | PapersAdda.com

Advertisement Placement

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.

More in interview-questions

More from PapersAdda

Share this article: