react hooks

Intermediate

React Hooks Intermediate Interview Preparation Guide

Congratulations on preparing for your intermediate React Hooks interview! This guide will help you master core concepts, ace common questions, write clean code, and adopt best practices. Let’s dive in!


1. Core Concepts

What are Hooks?

Hooks let you use state and other React features in functional components. They were introduced in React 16.8 to eliminate class components’ complexity.

Essential Hooks

HookPurposeKey Points
useStateManage local state in functional componentsinitializer runs once; updates trigger re-renders. Use objects/arrays carefully (spread syntax).
useEffectHandle side effects (HTTP requests, subscriptions, timers)Similar to componentDidMount, componentDidUpdate, and componentWillUnmount combined. Dependency array controls execution.
useContextAccess React Context without prop drillingPasses context value down the tree. Use for shared state (e.g., themes, auth).
useReducerManage complex state logic (alternative to useState)Ideal for state that’s interrelated or requires reducing functions.
useMemoMemoize expensive calculationsPrevents recalculation on every render. Depends on dependencies.
useCallbackMemoize functions passed as propsPrevents unnecessary re-renders of child components.
useRefAccess DOM nodes or preserve values between rendersref.current is mutable; doesn’t trigger re-renders.
useImperativeHandleCustomize the instance returned by refUsed rarely; for exposing methods to parent components.
useLayoutEffectEffect that runs synchronously after DOM mutation (like componentDidUpdate)Similar to useEffect but Before Paint.

Rules of Hooks

  1. Only Call Hooks at the Top Level – Don’t call Hooks inside loops, conditions, or nested functions.
  2. Only Call Hooks in React Functions – Use them in functional components or custom Hooks.

2. Common Interview Questions

Q1: Explain the difference between useEffect and componentDidMount.

  • useEffect:
    useEffect(() => {  
      // Runs after mount *and* on updates  
    }, []); // Empty array = runs **only after mount** (similar to `componentDidMount`)  
    
  • componentDidMount: Only runs once after initial render (no dependency control).

Key Point: useEffect with an empty dependency array mimics componentDidMount, but cleanup works like componentWillUnmount.

Q2: Why is the dependency array important in useEffect?

It determines when the effect runs:

  • Empty array []: Runs once after initial render.
  • Missing array: Runs after every render (potentially causing infinite loops).
  • With dependencies [a, b]: Runs when a or b change.

Common Pitfall: Missing dependencies leads to stale closures or unwanted side effects.

Q3: What’s the difference between useMemo and useCallback?

HookPurposeExample
useMemoMemoize values (e.g., computed data)const memoizedValue = useMemo(() => compute(a, b), [a, b]);
useCallbackMemoize functions (callbacks)const handleClick = useCallback(() => doSomething(a), [a]);

Q4: When should you use useReducer over useState?

Use useReducer when:

  • State is complex (e.g., multiple sub-values).
  • Logic for updates is interrelated.
  • You need debugging via dispatch actions.

Example:

const [state, dispatch] = useReducer((state, action) => {  
  switch (action.type) {  
    case 'INCREMENT':  
      return { count: state.count + 1 };  
    default:  
      return state;  
  }  
}, { count: 0 });  

Q5: What is a custom Hook, and why use it?

A custom Hook is a function that uses other Hooks and encapsulates reusable logic.

Benefits:

  • Reusability across components.
  • Shared cleanup, memoization, and state logic.

Example:

// useLocalStorage.js  
export default function useLocalStorage(key, initialValue) {  
  const [value, setValue] = useState(() => {  
    const storedValue = localStorage.getItem(key);  
    return storedValue ? JSON.parse(storedValue) : initialValue;  
  });  

  useEffect(() => {  
    localStorage.setItem(key, JSON.stringify(value));  
  }, [key, value]);  

  return [value, setValue];  
}  

3. Code Examples

Example 1: Fetching Data with useEffect

import { useState, useEffect } from 'react';  

function UserList() {  
  const [users, setUsers] = useState([]);  
  const [loading, setLoading] = useState(true);  

  useEffect(() => {  
    const fetchData = async () => {  
      const res = await fetch('https://api.example.com/users');  
      const data = await res.json();  
      setUsers(data);  
      setLoading(false);  
    };  

    fetchData();  

    // Cleanup (optional):  
    return () => {  
      // Cancel pending requests if component unmounts  
    };  
  }, []);  

  if (loading) return <p>Loading...</p>;  

  return (  
    <ul>  
      {users.map(user => (  
        <li key={user.id}>{user.name}</li>  
      ))}  
    </ul>  
  );  
}  

Example 2: Debouncing Input with useCallback and useEffect

function SearchInput() {  
  const [query, setQuery] = useState('');  
  const [debouncedQuery, setDebouncedQuery] = useState(query);  

  // Debounce logic using useEffect  
  useEffect(() => {  
    const timer = setTimeout(() => {  
      setDebouncedQuery(query);  
    }, 500);  

    return () => clearTimeout(timer);  
  }, [query]);  

  // Memoized search function  
  const handleSearch = useCallback(() => {  
    console.log('Searching for:', debouncedQuery);  
    // API call here  
  }, [debouncedQuery]);  

  return (  
    <div>  
      <input  
        type="text"  
        value={query}  
        onChange={(e) => setQuery(e.target.value)}  
      />  
      <button onClick={handleSearch}>Search</button>  
    </div>  
  );  
}  

4. Best Practices

Do’s

  1. Split complex state into custom Hooks to improve readability.
  2. Minimize dependencies in useEffect/useMemo to avoid unnecessary runs.
  3. Use useRef for mutable values that don’t trigger re-renders (e.g., timers, DOM refs).
  4. Leverage useReducer for state that requires multiple update steps.
  5. Add exhaustive dependency arrays (use ESLint react-hooks/exhaustive-deps).

Don’ts

  1. Avoid missing dependency arrays – it leads to stale closures or bugs.
  2. Don’t over-memoize – it adds overhead and can hurt performance.
  3. Never call Hooks inside loops/conditions (violates Rules of Hooks).
  4. Avoid storing large objects directly in useState – use references or useRef instead.

Performance Tips

  • Use React.memo for components receiving memoized props (useCallback/useMemo).
  • Profile with React DevTools to identify unnecessary re-renders.
  • Split effects into smaller units to isolate side effects.

Final Tips for the Interview

  • Practice debugging: Explain how you’d debug a stale closure or infinite loop.
  • Discuss trade-offs: When to use useState vs. useReducer vs. Context.
  • Show real-world experience: Describe a custom Hook you’ve built or a complex effect you’ve managed.

You’ve got this! 💪 Remember: understand the “why” behind each Hook, and you’ll ace the interview!

Ready for a new challenge?

Start a new session to explore different topics or increase the difficulty level.

Start New Session
react hooks Preparation Guide | Interview Preparation