This chapter provides a comprehensive collection of React interview questions and answers, organized by difficulty level. These questions cover all aspects of React development and will help you prepare for technical interviews.
Beginner Level Questions
1. What is React and why is it used?
Answer: React is a JavaScript library for building user interfaces, particularly web applications. It was developed by Facebook and is now maintained by Meta and the open-source community.
Key reasons for using React:
- Component-based architecture: Build encapsulated components that manage their own state
- Virtual DOM: Efficient updates and rendering
- Declarative: Describe what the UI should look like for any given state
- Reusable components: Write once, use anywhere
- Strong ecosystem: Large community and extensive third-party libraries
2. What is JSX?
Answer: JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code within JavaScript. It makes React components more readable and easier to write.
// JSX
const element = <h1>Hello, World!</h1>;
// Equivalent JavaScript
const element = React.createElement('h1', null, 'Hello, World!');
Key features of JSX:
- Must return a single parent element
- Use className instead of class
- Use htmlFor instead of for
- JavaScript expressions in curly braces {}
3. What are React Components?
Answer: Components are the building blocks of React applications. They are reusable pieces of UI that can accept inputs (props) and return React elements.
Types of components:
// Functional Component
function Welcome(props) {
 return <h1>Hello, {props.name}!</h1>;
}
// Class Component
class Welcome extends React.Component {
 render() {
  return <h1>Hello, {this.props.name}!</h1>;
 }
}
4. What is the difference between state and props?
Answer:
Props | State |
Read-only | Mutable |
Passed from parent | Local to component |
Cannot be modified by component | Can be modified using setState |
Used for component communication | Used for component’s internal data |
// Props example
function Child({ name, age }) {
 return <p>{name} is {age} years old</p>;
}
// State example
function Counter() {
 const [count, setCount] = useState(0);
Â
 return (
  <div>
   <p>Count: {count}</p>
   <button onClick={() => setCount(count + 1)}>
    Increment
   </button>
  </div>
 );
}
5. What is the Virtual DOM?
Answer: The Virtual DOM is a JavaScript representation of the real DOM. React uses it to optimize updates by:
- Creating a virtual representation of the UI
- Comparing (diffing) the new virtual DOM with the previous version
- Updating only the parts that changed in the real DOM
Benefits:
- Faster updates
- Better performance
- Batch updates
- Predictable behavior
Intermediate Level Questions
6. What are React Hooks and why were they introduced?
Answer: Hooks are functions that allow you to use state and other React features in functional components. They were introduced in React 16.8.
Common hooks:
import React, { useState, useEffect, useContext } from 'react';
function MyComponent() {
 // useState hook
 const [count, setCount] = useState(0);
Â
 // useEffect hook
 useEffect(() => {
  document.title = `Count: ${count}`;
 }, [count]);
Â
 return (
  <div>
   <p>Count: {count}</p>
   <button onClick={() => setCount(count + 1)}>
    Increment
   </button>
  </div>
 );
}
Why hooks were introduced:
- Reuse stateful logic between components
- Simplify component logic
- Avoid “wrapper hell” with HOCs
- Better performance optimization
7. Explain the useEffect hook with examples
Answer: useEffect is used for side effects in functional components. It combines componentDidMount, componentDidUpdate, and componentWillUnmount.
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
 const [user, setUser] = useState(null);
Â
 // Effect runs after every render
 useEffect(() => {
  fetchUser(userId).then(setUser);
 });
Â
 // Effect runs only once (on mount)
 useEffect(() => {
  console.log('Component mounted');
 }, []);
Â
 // Effect runs when userId changes
 useEffect(() => {
  fetchUser(userId).then(setUser);
 }, [userId]);
Â
 // Cleanup function
 useEffect(() => {
  const timer = setInterval(() => {
   console.log('Timer tick');
  }, 1000);
 Â
  return () => clearInterval(timer); // Cleanup
 }, []);
Â
 return <div>{user ? user.name : 'Loading...'}</div>;
}
8. What is React Context API?
Answer: Context API provides a way to pass data through the component tree without having to pass props down manually at every level.
import React, { createContext, useContext, useState } from 'react';
// Create context
const ThemeContext = createContext();
// Provider component
function ThemeProvider({ children }) {
 const [theme, setTheme] = useState('light');
Â
 return (
  <ThemeContext.Provider value={{ theme, setTheme }}>
   {children}
  </ThemeContext.Provider>
 );
}
// Consumer component
function ThemedButton() {
 const { theme, setTheme } = useContext(ThemeContext);
Â
 return (
  <button
   style={{
    background: theme === 'dark' ? '#333' : '#fff',
    color: theme === 'dark' ? '#fff' : '#333'
   }}
   onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
  >
   Toggle Theme
  </button>
 );
}
9. What is the difference between controlled and uncontrolled components?
Answer:
Controlled Components: React controls the form data through state.
function ControlledInput() {
 const [value, setValue] = useState('');
Â
 return (
  <input
   value={value}
   onChange={(e) => setValue(e.target.value)}
  />
 );
}
Uncontrolled Components: DOM handles the form data, React uses refs to access values.
function UncontrolledInput() {
 const inputRef = useRef();
Â
 const handleSubmit = () => {
  console.log(inputRef.current.value);
 };
Â
 return (
  <div>
   <input ref={inputRef} />
   <button onClick={handleSubmit}>Submit</button>
  </div>
 );
}
10. What are Higher-Order Components (HOCs)?
Answer: HOCs are functions that take a component and return a new component with enhanced functionality.
// HOC that adds loading functionality
function withLoading(WrappedComponent) {
 return function WithLoadingComponent({ isLoading, ...props }) {
  if (isLoading) {
   return <div>Loading...</div>;
  }
  return <WrappedComponent {...props} />;
 };
}
// Usage
const UserListWithLoading = withLoading(UserList);
function App() {
 const [isLoading, setIsLoading] = useState(true);
Â
 return (
  <UserListWithLoading
   isLoading={isLoading}
   users={users}
  />
 );
}
Advanced Level Questions
11. Explain React’s reconciliation process
Answer: Reconciliation is the process React uses to update the DOM efficiently by comparing the new virtual DOM tree with the previous one.
Key concepts:
- Diffing Algorithm: React compares trees level by level
- Keys: Help React identify which items have changed
- Element Types: Different types result in complete re-renders
// Without keys (inefficient)
{items.map(item => <Item data={item} />)}
// With keys (efficient)
{items.map(item => <Item key={item.id} data={item} />)}
12. What is React.memo() and when should you use it?
Answer: React.memo() is a higher-order component that memoizes the result of a component and re-renders only when props change.
import React, { memo } from 'react';
const ExpensiveComponent = memo(function ExpensiveComponent({ data }) {
 console.log('Rendering ExpensiveComponent');
Â
 return (
  <div>
   {data.map(item => (
    <div key={item.id}>{item.name}</div>
   ))}
  </div>
 );
});
// Custom comparison function
const MyComponent = memo(function MyComponent({ user }) {
 return <div>{user.name}</div>;
}, (prevProps, nextProps) => {
 return prevProps.user.id === nextProps.user.id;
});
When to use:
- Component renders frequently with same props
- Component has expensive computations
- Parent component re-renders often
13. Explain useMemo and useCallback hooks
Answer:
useMemo: Memoizes expensive calculations
import React, { useMemo, useState } from 'react';
function ExpensiveCalculation({ items }) {
 const [filter, setFilter] = useState('');
Â
 const filteredItems = useMemo(() => {
  console.log('Filtering items...');
  return items.filter(item =>
   item.name.toLowerCase().includes(filter.toLowerCase())
  );
 }, [items, filter]);
Â
 return (
  <div>
   <input
    value={filter}
    onChange={(e) => setFilter(e.target.value)}
   />
   {filteredItems.map(item => (
    <div key={item.id}>{item.name}</div>
   ))}
  </div>
 );
}
useCallback: Memoizes functions to prevent unnecessary re-renders
import React, { useCallback, useState } from 'react';
function Parent() {
 const [count, setCount] = useState(0);
 const [name, setName] = useState('');
Â
 const handleClick = useCallback(() => {
  setCount(count + 1);
 }, [count]);
Â
 return (
  <div>
   <input
    value={name}
    onChange={(e) => setName(e.target.value)}
   />
   <Child onClick={handleClick} />
  </div>
 );
}
const Child = React.memo(({ onClick }) => {
 console.log('Child rendered');
 return <button onClick={onClick}>Click me</button>;
});
14. What are Error Boundaries?
Answer: Error boundaries are React components that catch JavaScript errors anywhere in their child component tree and display a fallback UI.
class ErrorBoundary extends React.Component {
 constructor(props) {
  super(props);
  this.state = { hasError: false, error: null };
 }
Â
 static getDerivedStateFromError(error) {
  return { hasError: true, error };
 }
Â
 componentDidCatch(error, errorInfo) {
  console.error('Error caught by boundary:', error, errorInfo);
 }
Â
 render() {
  if (this.state.hasError) {
   return (
    <div>
     <h2>Something went wrong.</h2>
     <details>
      {this.state.error && this.state.error.toString()}
     </details>
    </div>
   );
  }
 Â
  return this.props.children;
 }
}
// Usage
function App() {
 return (
  <ErrorBoundary>
   <MyComponent />
  </ErrorBoundary>
 );
}
15. Explain React Suspense and lazy loading
Answer: Suspense allows components to “wait” for something before rendering, commonly used with lazy loading.
import React, { Suspense, lazy } from 'react';
// Lazy load components
const LazyComponent = lazy(() => import('./LazyComponent'));
const AnotherLazyComponent = lazy(() =>
 import('./AnotherComponent').then(module => ({
  default: module.AnotherComponent
 }))
);
function App() {
 return (
  <div>
   <Suspense fallback={<div>Loading...</div>}>
    <LazyComponent />
   </Suspense>
  Â
   <Suspense fallback={<div>Loading another component...</div>}>
    <AnotherLazyComponent />
   </Suspense>
  </div>
 );
}
With React Router:
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));
function App() {
 return (
  <Router>
   <Suspense fallback={<div>Loading page...</div>}>
    <Switch>
     <Route path="/" exact component={Home} />
     <Route path="/about" component={About} />
    </Switch>
   </Suspense>
  </Router>
 );
}
Expert Level Questions
16. How would you optimize a React application for performance?
Answer: Multiple strategies can be employed:
1. Code Splitting:
// Route-based splitting
const Home = lazy(() => import('./Home'));
const Profile = lazy(() => import('./Profile'));
// Component-based splitting
const HeavyComponent = lazy(() => import('./HeavyComponent'));
2. Memoization:
// Component memoization
const ExpensiveList = React.memo(({ items }) => {
 return items.map(item => <Item key={item.id} {...item} />);
});
// Value memoization
const processedData = useMemo(() => {
 return heavyComputation(data);
}, [data]);
3. Virtual Scrolling:
import { FixedSizeList as List } from 'react-window';
function VirtualizedList({ items }) {
 const Row = ({ index, style }) => (
  <div style={style}>
   {items[index].name}
  </div>
 );
Â
 return (
  <List
   height={400}
   itemCount={items.length}
   itemSize={35}
  >
   {Row}
  </List>
 );
}
4. Bundle Analysis:
# Analyze bundle size
npm run build -- --analyze
17. What is the difference between useLayoutEffect and useEffect?
Answer:
useEffect: Runs asynchronously after render is committed to screen
useLayoutEffect: Runs synchronously after all DOM mutations
import React, { useEffect, useLayoutEffect, useState } from 'react';
function ComparisonComponent() {
 const [width, setWidth] = useState(0);
Â
 // This might cause flickering
 useEffect(() => {
  const updateWidth = () => {
   setWidth(window.innerWidth);
  };
 Â
  updateWidth();
  window.addEventListener('resize', updateWidth);
 Â
  return () => window.removeEventListener('resize', updateWidth);
 }, []);
Â
 // This prevents flickering for DOM measurements
 useLayoutEffect(() => {
  const element = document.getElementById('myElement');
  if (element) {
   // Measure and update before paint
   const rect = element.getBoundingClientRect();
   setWidth(rect.width);
  }
 }, []);
Â
 return <div id="myElement">Width: {width}px</div>;
}
18. Explain React’s Concurrent Features
Answer: React 18 introduced concurrent features for better user experience:
1. Concurrent Rendering:
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
2. Transitions:
import { startTransition, useTransition } from 'react';
function SearchPage() {
 const [query, setQuery] = useState('');
 const [results, setResults] = useState([]);
 const [isPending, startTransition] = useTransition();
Â
 const handleSearch = (value) => {
  setQuery(value); // Urgent update
 Â
  startTransition(() => {
   setResults(searchData(value)); // Non-urgent update
  });
 };
Â
 return (
  <div>
   <input
    value={query}
    onChange={(e) => handleSearch(e.target.value)}
   />
   {isPending && <div>Searching...</div>}
   <SearchResults results={results} />
  </div>
 );
}
3. useDeferredValue:
import { useDeferredValue } from 'react';
function SearchResults({ query }) {
 const deferredQuery = useDeferredValue(query);
 const results = useMemo(() =>
  searchData(deferredQuery), [deferredQuery]
 );
Â
 return (
  <div>
   {results.map(result => (
    <div key={result.id}>{result.title}</div>
   ))}
  </div>
 );
}
19. How would you implement a custom hook?
Answer: Custom hooks allow you to extract component logic into reusable functions:
// Custom hook for API calls
function useApi(url) {
 const [data, setData] = useState(null);
 const [loading, setLoading] = useState(true);
 const [error, setError] = useState(null);
Â
 useEffect(() => {
  const fetchData = async () => {
   try {
    setLoading(true);
    const response = await fetch(url);
    const result = await response.json();
    setData(result);
   } catch (err) {
    setError(err);
   } finally {
    setLoading(false);
   }
  };
 Â
  fetchData();
 }, [url]);
Â
 return { data, loading, error };
}
// Custom hook for local storage
function useLocalStorage(key, initialValue) {
 const [storedValue, setStoredValue] = useState(() => {
  try {
   const item = window.localStorage.getItem(key);
   return item ? JSON.parse(item) : initialValue;
  } catch (error) {
   return initialValue;
  }
 });
Â
 const setValue = (value) => {
  try {
   const valueToStore = value instanceof Function
    ? value(storedValue)
    : value;
   setStoredValue(valueToStore);
   window.localStorage.setItem(key, JSON.stringify(valueToStore));
  } catch (error) {
   console.log(error);
  }
 };
Â
 return [storedValue, setValue];
}
// Usage
function MyComponent() {
 const { data, loading, error } = useApi('/api/users');
 const [name, setName] = useLocalStorage('name', '');
Â
 if (loading) return <div>Loading...</div>;
 if (error) return <div>Error: {error.message}</div>;
Â
 return (
  <div>
   <input
    value={name}
    onChange={(e) => setName(e.target.value)}
   />
   <UserList users={data} />
  </div>
 );
}
20. Explain React’s reconciliation algorithm and keys
Answer: React uses a heuristic O(n) algorithm for reconciliation:
Rules:
- Different element types result in complete rebuilds
- Keys help identify which items have changed
- React assumes most changes are local
// Bad: No keys
function TodoList({ todos }) {
 return (
  <ul>
   {todos.map(todo => (
    <li>{todo.text}</li> // React can't track items
   ))}
  </ul>
 );
}
// Bad: Index as key
function TodoList({ todos }) {
 return (
  <ul>
   {todos.map((todo, index) => (
    <li key={index}>{todo.text}</li> // Unstable keys
   ))}
  </ul>
 );
}
// Good: Stable, unique keys
function TodoList({ todos }) {
 return (
  <ul>
   {todos.map(todo => (
    <li key={todo.id}>{todo.text}</li> // Stable unique keys
   ))}
  </ul>
 );
}
Impact of keys:
// Without proper keys, React might:
// 1. Re-render entire list
// 2. Lose component state
// 3. Cause performance issues
// With proper keys, React can:
// 1. Identify moved items
// 2. Preserve component state
// 3. Optimize updates
Common Debugging Questions
21. How do you debug React applications?
Answer: Multiple tools and techniques:
1. React DevTools:
// Install React DevTools browser extension // Components tab: Inspect component tree // Profiler tab: Performance analysis |
2. Console Logging:
function MyComponent({ data }) { console.log(‘Component rendered with:’, data); useEffect(() => { console.log(‘Effect ran’); }, [data]); return <div>{data.name}</div>; } |
3. Error Boundaries:
class ErrorBoundary extends React.Component { componentDidCatch(error, errorInfo) { console.error(‘Error details:’, { error, errorInfo }); // Send to error reporting service } render() { // … error UI } } |
4. Performance Profiling:
import { Profiler } from ‘react’; function onRenderCallback(id, phase, actualDuration) { console.log(‘Render info:’, { id, phase, actualDuration }); } <Profiler id=”MyComponent” onRender={onRenderCallback}> <MyComponent /> </Profiler> |
22. What are common React performance issues?
Answer:
1. Unnecessary Re-renders:
// Problem: Object/array creation in render function BadComponent() { return <Child data={{ name: ‘John’ }} />; // New object every render } // Solution: Move outside or use useMemo const userData = { name: ‘John’ }; function GoodComponent() { return <Child data={userData} />; } |
2. Missing Dependencies:
// Problem: Stale closure function BadComponent() { const [count, setCount] = useState(0); useEffect(() => { const timer = setInterval(() => { setCount(count + 1); // Stale count }, 1000); return () => clearInterval(timer); }, []); // Missing dependency return <div>{count}</div>; } // Solution: Use functional updates function GoodComponent() { const [count, setCount] = useState(0); useEffect(() => { const timer = setInterval(() => { setCount(prev => prev + 1); // Fresh value }, 1000); return () => clearInterval(timer); }, []); return <div>{count}</div>; } |
Coding Challenge Questions
23. Implement a custom useDebounce hook
Answer:
import { useState, useEffect } from ‘react’; function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(handler); }; }, [value, delay]); return debouncedValue; } // Usage function SearchComponent() { const [searchTerm, setSearchTerm] = useState(”); const debouncedSearchTerm = useDebounce(searchTerm, 300); useEffect(() => { if (debouncedSearchTerm) { // Perform search console.log(‘Searching for:’, debouncedSearchTerm); } }, [debouncedSearchTerm]); return ( <input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder=”Search…” /> ); } |
24. Create a Modal component with portal
Answer:
import { useState, useEffect } from ‘react’; import { createPortal } from ‘react-dom’; function Modal({ isOpen, onClose, children }) { const [modalRoot, setModalRoot] = useState(null); useEffect(() => { const root = document.getElementById(‘modal-root’) || document.createElement(‘div’); if (!document.getElementById(‘modal-root’)) { root.id = ‘modal-root’; document.body.appendChild(root); } setModalRoot(root); return () => { if (root.childNodes.length === 0) { document.body.removeChild(root); } }; }, []); useEffect(() => { const handleEscape = (e) => { if (e.key === ‘Escape’) { onClose(); } }; if (isOpen) { document.addEventListener(‘keydown’, handleEscape); document.body.style.overflow = ‘hidden’; } return () => { document.removeEventListener(‘keydown’, handleEscape); document.body.style.overflow = ‘unset’; }; }, [isOpen, onClose]); if (!isOpen || !modalRoot) return null; return createPortal( <div className=”modal-overlay” onClick={onClose} style={{ position: ‘fixed’, top: 0, left: 0, right: 0, bottom: 0, backgroundColor: ‘rgba(0, 0, 0, 0.5)’, display: ‘flex’, alignItems: ‘center’, justifyContent: ‘center’, zIndex: 1000 }} > <div className=”modal-content” onClick={(e) => e.stopPropagation()} style={{ backgroundColor: ‘white’, padding: ’20px’, borderRadius: ‘8px’, maxWidth: ‘90%’, maxHeight: ‘90%’, overflow: ‘auto’ }} > {children} </div> </div>, modalRoot ); } // Usage function App() { const [isModalOpen, setIsModalOpen] = useState(false); return ( <div> <button onClick={() => setIsModalOpen(true)}> Open Modal </button> <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} > <h2>Modal Content</h2> <p>This is a modal dialog.</p> <button onClick={() => setIsModalOpen(false)}> Close </button> </Modal> </div> ); } |
Tips for React Interviews
Preparation Strategy
- Understand the basics thoroughly
- Component lifecycle
- State management
- Props and state differences
- Event handling
- Practice coding problems
- Build small components
- Implement hooks
- Handle edge cases
- Know the ecosystem
- React Router
- State management libraries
- Testing frameworks
- Build tools
- Stay updated
- React 18 features
- Concurrent features
- Future roadmap
Common Mistakes to Avoid
- In interviews:
- Don’t memorize answers without understanding
- Don’t skip explaining your thought process
- Don’t ignore edge cases in coding problems
- In code:
- Don’t mutate state directly
- Don’t forget to handle cleanup in useEffect
- Don’t create objects/functions in render without memoization
Key Points to Remember
- Always explain your reasoning
- Consider performance implications
- Think about user experience
- Handle error cases
- Write clean, readable code
- Test your solutions mentally
This comprehensive collection of questions and answers covers the essential React concepts you’ll encounter in interviews, from basic component creation to advanced performance optimization techniques.