Skip to content

React Tutorial React Hooks

Common React Hook Mistakes and How to Fix Them

Lesson 70 of 156 2 min read By Deepak Paswan Updated Oct 10, 2026

Runnable lesson · press Try it on the examples to edit and run them here.

Most hook bugs come from a short list of mistakes. Learn to recognise each one, and you can fix problems quickly.

1. Calling hooks conditionally

App.jsx
JSX
if (user) {
  const [name, setName] = useState(user.name);  // Error: order changes between renders
}

Fix: call hooks unconditionally at the top; put the if inside or below. See Rules of Hooks.

2. Stale closures from missing dependencies

The effect or callback keeps using an old value.

App.jsx
JSX
useEffect(() => {
  const id = setInterval(() => console.log(count), 1000); // always logs the first count
  return () => clearInterval(id);
}, []);   // count is missing

Fix: add the dependency, or use an updater function, or a ref for the latest value. See Effect Dependencies.

3. Forgetting cleanup

Timers, listeners and subscriptions keep running after the component is gone.

Fix: return a cleanup function that undoes what the effect started. See Cleanup Functions.

4. Infinite loops

App.jsx
JSX
useEffect(() => {
  setData(fetchedData.map(transform));   // new array every time
}, [data]);                              // depends on what it sets

Fix: do not depend on the state you set, or calculate the value during render. Also watch for objects and arrays created during render that are used as dependencies.

5. Using an effect for something that is not an effect

Copying props to state, calculating totals in an effect, reacting to clicks in an effect. Fix: Do You Need an Effect?

6. Mutating state

App.jsx
JSX
items.push(newItem);
setItems(items);       // same reference: no re-render

Fix: always set a new array or object. See Avoiding State Mutation.

7. Reading state right after setting it

App.jsx
JSX
setCount(count + 1);
console.log(count);    // still the old value

Fix: use the new value you computed, or an updater function. See State as a Snapshot.

8. Ignoring race conditions in fetch effects

An older, slower response overwrites a newer one. Fix: an ignore flag in cleanup or AbortController. See Fetching Data with Effects.

9. Over-memoizing

useMemo and useCallback on everything adds code and bugs and rarely makes anything faster. Fix: measure first, see useMemo.

10. Making an effect async

App.jsx
JSX
useEffect(async () => {   // wrong: returns a promise, not a cleanup function
  const res = await fetch(url);
}, []);

React expects the effect to return nothing or a cleanup function, not a promise. Fix: define an async function inside and call it:

App.jsx
JSX
function App() {
  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    let ignore = false;
    async function load() {
      const res = await fetch("data:application/json,[1,2,3]");
      const list = await res.json();
      if (!ignore) setCount(list.length);
    }
    load();
    return () => { ignore = true; };
  }, []);

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

11. Changing the shape of state’s type

Starting with useState() (undefined) and later setting a string makes inputs switch from uncontrolled to controlled. Fix: start with "", 0, [] or null deliberately.

12. Putting hooks in loops or callbacks

App.jsx
JSX
items.map((item) => {
  const [open, setOpen] = useState(false);   // not allowed
  return <li>{item}</li>;
});

Fix: extract an Item component; each instance then has its own hook.

App.jsx
JSX
function Item({ name }) {
  const [open, setOpen] = React.useState(false);
  return (
    <li>
      <button onClick={() => setOpen(!open)}>Toggle {name}</button> {name}{open ? " (open)" : ""}
    </li>
  );
}

function App() {
  return <ul>{["Ada", "Grace"].map((n) => <Item key={n} name={n} />)}</ul>;
}

A debugging routine

  1. Read the first error message in the console; fix that one first.
  2. Check the Rules of Hooks: any hook after a condition or return?
  3. Add console.log to see which render has the unexpected value.
  4. Check dependency arrays for missing values.
  5. Use React DevTools to inspect props, state and re-renders. See Debugging React Applications.

Key points

  • Most hook bugs are one of: conditional hooks, stale closures, missing cleanup, loops, unnecessary effects, mutation.
  • Fix the code instead of silencing the linter.
  • Keep it simple: fewer hooks mean fewer bugs.