❌ useEffect Infinite Loop

Problem

useEffect(() => {
  setCount(count + 1);
}, [count]);

Why it’s wrong setCount updates count, which triggers the effect again → infinite loop.

Fix

useEffect(() => {
  setCount(prev => prev + 1);
}, []);

❌ Stale Closure

Problem

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

Why it’s wrong The effect captures the initial value of count.

Fix

useEffect(() => {
  const id = setTimeout(() => {
    console.log(count);
  }, 1000);

  return () => clearTimeout(id);
}, [count]);

❌ Conditional Hooks

Problem

if (isLoggedIn) {
  useEffect(() => {
    fetchData();
  }, []);
}

Why it’s wrong Hooks must be called unconditionally and in the same order.

Fix

useEffect(() => {
  if (isLoggedIn) {
    fetchData();
  }
}, [isLoggedIn]);

❌ Async useEffect

Problem

useEffect(async () => {
  await fetchData();
}, []);

Why it’s wrong useEffect must return either nothing or a cleanup function — not a Promise.

Fix

useEffect(() => {
  const load = async () => {
    await fetchData();
  };

  load();
}, []);

❌ Missing Dependency

Problem

useEffect(() => {
  fetchData(id);
}, []);

Why it’s wrong id is used but not listed → stale data bug.

Fix

useEffect(() => {
  fetchData(id);
}, [id]);

⚠️ State Pitfalls

❌ setState Using Old State

Problem

setCount(count + 1);
setCount(count + 1);

Why it’s wrong Both updates read the same stale value.

Fix

setCount(prev => prev + 1);
setCount(prev => prev + 1);

❌ Derived State Anti-Pattern

Problem

const [fullName, setFullName] = useState(
  firstName + " " + lastName
);

Why it’s wrong State derived from state causes sync bugs.

Fix

const fullName = `${firstName} ${lastName}`;

❌ Using Ref Instead of State

Problem

const count = useRef(0);
return <p>{count.current}</p>;

Why it’s wrong Refs don’t trigger re-renders.

Fix

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

⚠️ Performance Pitfalls

❌ Wrong Key Usage

Problem

items.map((item, index) => (
  <li key={index}>{item}</li>
));

Why it’s wrong Index keys break reconciliation on reorder.

Fix

items.map(item => (
  <li key={item.id}>{item.name}</li>
));

❌ useMemo With Empty Deps

Problem

const value = useMemo(() => compute(data), []);

Why it’s wrong data changes won’t recompute.

Fix

const value = useMemo(() => compute(data), [data]);

❌ useCallback Trap

Problem

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

Why it’s wrong count is frozen to its initial value.

Fix

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

❌ Memoizing JSX

Problem

const view = useMemo(() => <Child />, []);

Why it’s wrong JSX creation is cheap — this adds complexity for nothing.

Fix

<Child />

❌ React.memo False Optimization

Problem

const Child = React.memo(({ user }) => {
  return <p>{user.name}</p>;
});

<Child user= />

Why it’s wrong New object reference → re-render anyway.

Fix

const user = useMemo(() => ({ name: "John" }), []);
<Child user={user} />

⚠️ Async & Data Pitfalls

❌ Race Condition

Problem

useEffect(() => {
  fetchUser(id).then(setUser);
}, [id]);

Why it’s wrong Older requests can overwrite newer ones.

Fix

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

  fetchUser(id).then(user => {
    if (active) setUser(user);
  });

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

⚠️ Context Pitfalls

❌ Context Re-render Explosion

Problem

<AuthContext.Provider value=>

Why it’s wrong New object → all consumers re-render.

Fix

const value = useMemo(() => ({ user, logout }), [user, logout]);

<AuthContext.Provider value={value}>

✅ Final Thought

Most React / Next.js bugs aren’t syntax issues — they’re state, closure, and lifecycle misunderstandings.

If you can explain why these break and how to fix them, you’re already thinking at a senior level.