Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
openaec-foundation avatar

React Errors Hooks

  • 11 installs
  • 6 repo stars
  • Updated July 8, 2026
  • openaec-foundation/react-claude-skill-package

Helps with frontend development tasks.

About

react-errors-hooks is a Claude Code skill for frontend development. It helps solo builders move faster with AI-assisted development.

  • react-errors-hooks
  • Frontend Development
  • AI-coding skill

React Errors Hooks by the numbers

  • 11 all-time installs (skills.sh)
  • Ranked #1,658 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/openaec-foundation/react-claude-skill-package --skill react-errors-hooks

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs11
repo stars6
Last updatedJuly 8, 2026
Repositoryopenaec-foundation/react-claude-skill-package

What it does

Helps with frontend development tasks.

Files

SKILL.mdMarkdownGitHub ↗

react-errors-hooks

Quick Reference: Error Diagnostic Table

SymptomCauseFix
React has detected a change in the order of HooksConditional hook call or early return before hooksMove ALL hooks above any conditional logic or returns
Invalid hook callHook called in regular function, class, or nested scopeONLY call hooks from function components or custom hooks
Too many re-renderssetState called during render bodyMove state update into event handler or useEffect
Component re-renders infinitely with useEffectMissing dependency array or new object/array in depsAdd [] deps, destructure objects to primitives, or use useMemo
State shows old value in setTimeout/setIntervalStale closure captures initial state valueUse functional updater setState(prev => ...) or useRef
State shows old value in event listenerEvent listener closed over stale stateUse useRef to hold latest value, or re-register listener
Warning: Can't perform a React state update on unmounted componentAsync callback updates state after unmountReturn cleanup function from useEffect with ignore flag
React Hook useEffect has a missing dependencyeslint-plugin-react-hooks exhaustive-deps violationAdd the dependency, move code into effect, or restructure
React Hook "useX" is called conditionallyHook inside if/else, loop, or after early returnRestructure to call hook unconditionally at top level
React Hook "useX" cannot be called in a class componentHook used inside a class componentConvert to function component or extract hook logic
Effect fires twice in developmentStrictMode double-invocation (expected behavior)Ensure cleanup function properly reverses setup
useFormStatus always returns pending: falseHook called in same component as <form>Move useFormStatus into a child component rendered inside the form

---

Rules of Hooks Violations

Rule 1: ALWAYS Call Hooks at the Top Level

NEVER call hooks inside:

  • Conditions (if, ternary, &&)
  • Loops (for, while, do...while)
  • Nested functions or callbacks
  • try/catch/finally blocks
  • After conditional return statements
  • Event handler functions
  • Functions passed to useMemo, useReducer, or useEffect

Exception: use() (React 19 only) CAN be called inside conditionals and loops.

// WRONG: Hook after conditional return
function Profile({ userId }: { userId: string | null }) {
  if (!userId) return <p>No user selected</p>;
  const [user, setUser] = useState<User | null>(null); // ERROR
  // ...
}

// CORRECT: Hook before any conditional logic
function Profile({ userId }: { userId: string | null }) {
  const [user, setUser] = useState<User | null>(null);
  if (!userId) return <p>No user selected</p>;
  // ...
}

Rule 2: ALWAYS Call Hooks from React Functions

NEVER call hooks from regular JavaScript functions.

// WRONG: Hook in a utility function
function getWindowSize() {
  const [size, setSize] = useState({ width: 0, height: 0 }); // ERROR
  return size;
}

// CORRECT: Prefix with "use" to make it a custom hook
function useWindowSize() {
  const [size, setSize] = useState({ width: 0, height: 0 });
  // ... effect to track window size
  return size;
}

---

Infinite Re-render Loops

Pattern 1: setState in Render Body

// WRONG: Causes "Too many re-renders"
function Counter() {
  const [count, setCount] = useState(0);
  setCount(count + 1); // Triggers re-render during render
  return <p>{count}</p>;
}

// CORRECT: Update in event handler
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

Pattern 2: useEffect Without Dependency Array

// WRONG: Runs every render, setState triggers re-render, infinite loop
function DataLoader() {
  const [data, setData] = useState<string[]>([]);
  useEffect(() => {
    fetchData().then(setData); // No deps = runs every render
  });
  return <List items={data} />;
}

// CORRECT: Specify dependency array
function DataLoader() {
  const [data, setData] = useState<string[]>([]);
  useEffect(() => {
    fetchData().then(setData);
  }, []); // Runs once on mount
  return <List items={data} />;
}

Pattern 3: New Object/Array Reference in Dependencies

// WRONG: New object every render = deps always "change"
function ChatRoom({ roomId }: { roomId: string }) {
  const options = { serverUrl: "https://localhost:1234", roomId };
  useEffect(() => {
    const conn = createConnection(options);
    conn.connect();
    return () => conn.disconnect();
  }, [options]); // New reference every render = infinite loop
}

// CORRECT: Move object creation inside effect
function ChatRoom({ roomId }: { roomId: string }) {
  useEffect(() => {
    const options = { serverUrl: "https://localhost:1234", roomId };
    const conn = createConnection(options);
    conn.connect();
    return () => conn.disconnect();
  }, [roomId]); // Primitive dependency = stable
}

---

Stale Closures

Timer Callbacks Reading Old State

// WRONG: interval callback captures initial count (always 0)
function Timer() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1); // Always sets to 1
    }, 1000);
    return () => clearInterval(id);
  }, []); // count not in deps, closure captures 0
}

// CORRECT: Use functional updater
function Timer() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(prev => prev + 1); // Reads latest state
    }, 1000);
    return () => clearInterval(id);
  }, []);
}

Event Listeners with Stale Refs

// WRONG: Handler captures stale state
function ScrollTracker() {
  const [scrollY, setScrollY] = useState(0);
  const [message, setMessage] = useState("");

  useEffect(() => {
    function handleScroll() {
      setScrollY(window.scrollY);
      setMessage(`Scrolled ${message.length} chars`); // Stale message
    }
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []); // message not in deps
}

// CORRECT: Use ref for latest value
function ScrollTracker() {
  const [scrollY, setScrollY] = useState(0);
  const [message, setMessage] = useState("");
  const messageRef = useRef(message);
  messageRef.current = message;

  useEffect(() => {
    function handleScroll() {
      setScrollY(window.scrollY);
      setMessage(`Scrolled ${messageRef.current.length} chars`);
    }
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);
}

---

useEffect Cleanup Mistakes

Missing Cleanup: Subscription Leak

// WRONG: No cleanup, listener accumulates on every re-render
useEffect(() => {
  window.addEventListener("resize", handleResize);
}, [handleResize]);

// CORRECT: Return cleanup function
useEffect(() => {
  window.addEventListener("resize", handleResize);
  return () => window.removeEventListener("resize", handleResize);
}, [handleResize]);

Missing Cleanup: Async Race Condition

// WRONG: Stale responses overwrite fresh data
useEffect(() => {
  fetchUser(userId).then(user => setUser(user));
}, [userId]);

// CORRECT: Ignore flag prevents stale updates
useEffect(() => {
  let ignore = false;
  fetchUser(userId).then(user => {
    if (!ignore) setUser(user);
  });
  return () => { ignore = true; };
}, [userId]);

Missing Cleanup: Timer Leak

// WRONG: Interval never cleared on unmount
useEffect(() => {
  setInterval(() => setCount(c => c + 1), 1000);
}, []);

// CORRECT: Store ID and clear on cleanup
useEffect(() => {
  const id = setInterval(() => setCount(c => c + 1), 1000);
  return () => clearInterval(id);
}, []);

---

useEffect Timing

HookFiresBlocks Paint?Use Case
useInsertionEffectBefore DOM mutationsYesCSS-in-JS libraries ONLY
useLayoutEffectAfter DOM mutations, before paintYesDOM measurement (tooltips, popovers)
useEffectAfter paintNoData fetching, subscriptions, most side effects

NEVER use useLayoutEffect unless you need to measure DOM before the browser paints. It blocks rendering and hurts performance.

NEVER use useInsertionEffect unless you are building a CSS-in-JS library.

---

eslint-plugin-react-hooks

exhaustive-deps: When to Fix vs Suppress

ALWAYS fix by default. Suppression is ONLY acceptable in these rare cases:

ScenarioAction
Missing primitive dependencyAdd it to the array
Missing object/function dependencyMove creation inside effect, or use useMemo/useCallback
Intentionally run only on mountUse [] and verify no reactive values are read
External stable reference (e.g., dispatch, ref)Safe to omit — setState, dispatch, and refs are stable
Genuinely need to read latest value without re-triggeringUse useRef to hold the value
// WRONG: Suppressing a real dependency
useEffect(() => {
  fetchData(query); // query IS reactive
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // Bug: runs with stale query

// CORRECT: Include the dependency
useEffect(() => {
  fetchData(query);
}, [query]);

---

"You Might Not Need an Effect"

NEVER use useEffect for these scenarios:

ScenarioWrong ApproachCorrect Approach
Data transformationuseEffect + setStateCalculate during render
Expensive computationuseEffect + setStateuseMemo
Reset state on prop changeuseEffect + setStatekey prop on component
Event-driven logicuseEffect watching statePut logic in event handler
Notify parent of changesuseEffect + parent callbackCall parent callback in event handler
External store subscriptionuseEffect + addEventListeneruseSyncExternalStore
App initializationuseEffect with []Module-level code or guarded top-level call

---

StrictMode Double-Invocation

In development, React StrictMode runs setup + cleanup + setup for every Effect. This is NOT a bug.

If your component breaks under StrictMode, your cleanup function does not properly reverse the setup:

// WRONG: Breaks under StrictMode (duplicate connections)
useEffect(() => {
  const conn = createConnection(roomId);
  conn.connect(); // Called twice, two connections open
}, [roomId]);

// CORRECT: Cleanup reverses setup
useEffect(() => {
  const conn = createConnection(roomId);
  conn.connect();
  return () => conn.disconnect(); // Second setup works clean
}, [roomId]);

---

Common useState Anti-Patterns

Anti-PatternProblemFix
useState(expensiveFn())Function runs every renderuseState(expensiveFn) — pass reference
obj.x = 5; setObj(obj)Same reference, no re-rendersetObj({...obj, x: 5}) — new object
arr.push(item); setArr(arr)Mutation, same referencesetArr([...arr, item])
useState(() => myFn) when storing a functionReact calls it as initializeruseState(() => () => myFn) — wrap in arrow
Reading state right after setStateState updates are asyncUse functional updater or useEffect

---

Reference Links

  • references/examples.md -- Hook error examples with complete wrong/correct code pairs
  • references/anti-patterns.md -- Comprehensive catalog of hook mistakes organized by category

Official Sources

  • https://react.dev/reference/rules/rules-of-hooks
  • https://react.dev/reference/react/useEffect
  • https://react.dev/reference/react/useState
  • https://react.dev/learn/you-might-not-need-an-effect
  • https://react.dev/learn/synchronizing-with-effects
  • https://react.dev/learn/removing-effect-dependencies

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.