DailyDevDiet

logo - dailydevdiet

Learn. Build. Innovate. Elevate your coding skills with dailydevdiet!

Chapter 39: React Interview Questions and Answers

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:

PropsState
Read-onlyMutable
Passed from parentLocal to component
Cannot be modified by componentCan be modified using setState
Used for component communicationUsed 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:

  1. Creating a virtual representation of the UI
  2. Comparing (diffing) the new virtual DOM with the previous version
  3. 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:

  1. Different element types result in complete rebuilds
  2. Keys help identify which items have changed
  3. 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

  1. Understand the basics thoroughly
    • Component lifecycle
    • State management
    • Props and state differences
    • Event handling
  2. Practice coding problems
    • Build small components
    • Implement hooks
    • Handle edge cases
  3. Know the ecosystem
    • React Router
    • State management libraries
    • Testing frameworks
    • Build tools
  4. Stay updated
    • React 18 features
    • Concurrent features
    • Future roadmap

Common Mistakes to Avoid

  1. In interviews:
    • Don’t memorize answers without understanding
    • Don’t skip explaining your thought process
    • Don’t ignore edge cases in coding problems
  2. 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.

Scroll to Top