logo
React Hooks - Interview Questions and Answers
What are some performance pitfalls when using Hooks?
Performance Pitfalls When Using Hooks & How to Fix Them

React Hooks make development easier, but misusing them can lead to performance issues like unnecessary re-renders, stale closures, and memory leaks. Here are some common pitfalls and how to fix them.


1. Unnecessary Re-renders Due to useState Updates :
Pitfall: Updating state with the same value :

If you call setState with the same value, React will still re-render the component.

function Counter() {
  const [count, setCount] = useState(0);

  console.log("Component re-rendered");

  return <button onClick={() => setCount(0)}>Set to 0</button>;
}

* Issue: Clicking the button re-renders even though the state hasn’t changed.

* Fix : Use a function update to check the current state.

<button onClick={() => setCount(prev => (prev === 0 ? prev : 0))}>Set to 0</button>

2. Infinite Loops in useEffect :
Pitfall: Updating state inside useEffect without proper dependencies
function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(count + 1); // Causes infinite loop
  }, [count]);

  return <p>Count: {count}</p>;
}

* Issue: Every time count updates, useEffect triggers another update.

* Fix: Use a function update or an appropriate dependency array.

useEffect(() => {
  setCount(prev => prev + 1);
}, []); //  Runs only once

3. Unnecessary Function Re-Creation in useEffect :
Pitfall : Declaring functions inside useEffect
function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const log = () => console.log(count); // Recreated every render
    document.addEventListener("click", log);

    return () => document.removeEventListener("click", log);
  }, [count]); // Memory leak risk
}

* Issue: Every re-render creates a new log function, causing multiple event listeners.

* Fix: Use useCallback to memoize the function.

const log = useCallback(() => console.log(count), [count]);

useEffect(() => {
  document.addEventListener("click", log);
  return () => document.removeEventListener("click", log);
}, [log]);

4. Stale State Due to Missing Dependencies in useEffect :
Pitfall: Using old state inside useEffect
useEffect(() => {
  console.log(count); //  May log an outdated count
}, []); //  Missing [count]

* Issue : The effect captures the initial value of count, not the latest.

* Fix : Always add dependencies to the array.

useEffect(() => {
  console.log(count);
}, [count]);

5. Over-Reliance on useMemo and useCallback :
Pitfall : Using useMemo and useCallback when unnecessary
const computedValue = useMemo(() => a * b, [a, b]); //  Unnecessary if computation is simple
const handleClick = useCallback(() => console.log("Clicked"), []); //  Doesn't need memoization

* Issue : These Hooks add complexity and should be used only for expensive computations.

* Fix : Use them only when necessary (e.g., for expensive calculations or preventing unnecessary prop changes).


6. Unnecessary Re-renders Due to Unstable Props :
Pitfall: Passing functions directly as props without memoization
function Parent() {
  return <Child onClick={() => console.log("Clicked")} />; //  New function every render
}

* Issue : Child re-renders every time even if onClick doesn’t change.

* Fix : Use useCallback to prevent unnecessary re-renders.

const handleClick = useCallback(() => console.log("Clicked"), []);
<Child onClick={handleClick} />;

7. Memory Leaks from Unmounted Components :
Pitfall: Updating state after component unmounts
useEffect(() => {
  fetchData().then(data => setState(data)); // Possible memory leak
}, []);

* Issue : If the component unmounts before fetchData completes, React tries to update an unmounted component.

* Fix : Use a cleanup function.

useEffect(() => {
  let isMounted = true;

  fetchData().then(data => {
    if (isMounted) setState(data);
  });

  return () => {
    isMounted = false;
  };
}, []);

Best Practices to Optimize Hooks
 Bad Practice Best Practice
Updating state unnecessarily Only update if values change
Missing dependencies in useEffect Always include necessary dependencies
Recreating functions inside components Use useCallback for stable functions
Using useMemo everywhere Use it only for expensive calculations
Updating state inside useEffect without cleanup Use cleanup functions to avoid memory leaks