DEV Community

Cover image for React Hooks and the Rules of Hooks – The Understanding That Finally Clicked for Me
Usama
Usama

Posted on

React Hooks and the Rules of Hooks – The Understanding That Finally Clicked for Me

Today, something finally clicked for me.

I had been using React Hooks for a long time, but if I’m being honest, I didn’t truly understand what they are, why React is so strict about their rules, and why some patterns that seem to work are still considered bad practice.

This blog is my attempt to write down what I personally understood, so that:

  • If I read this again after one or two weeks
  • Or if I get confused in the future

This post should bring me back to full clarity, not just surface-level knowledge.


What React Hooks Actually Are

The first misconception that got cleared was this:

Hooks are not just normal helper functions.

Hooks are special building functions provided by React.

They directly interact with React’s Fiber architecture.

Hooks are used to:

  • Register state with React
  • Register side effects
  • Connect component logic to React’s internal update system

That’s why:

  • Every hook starts with use (useState, useEffect, useRef, etc.)
  • React does not treat hooks like normal JavaScript functions

They are tightly coupled with how React renders and updates components.


Hooks and React Fiber – The Missing Mental Model

This was the real turning point for me.

Internally, React maintains a Fiber Tree.

For each component render:

  • React stores hooks in a linked list
  • The first hook call becomes the first node
  • The second hook call becomes the second node
  • And so on…

React does not identify hooks by name.

It identifies them by their order.

This single fact explains all the Rules of Hooks.


Overview of React Hooks

React provides many hooks, but practically speaking:

Most commonly used hooks:

  • useState
  • useEffect
  • useReducer
  • useContext

Performance / optimization hooks:

  • useRef
  • useCallback
  • useMemo
  • useTransition
  • useDeferredValue

Low-level or advanced hooks:

  • useSyncExternalStore
  • useInsertionEffect

Different hooks, different purposes —

but the same rules apply to all of them.


Rules of Hooks (The Part That Finally Made Sense)

✅ Rule 1: Only call hooks at the top level

This means:

  • No hooks inside if
  • No hooks inside loops
  • No hooks after return

❌ Hooks should never be conditionally executed.

Example of a tempting but wrong pattern:

if (imdbRating > 8) {
  const [isTop, setIsTop] = useState(true);
}
Enter fullscreen mode Exit fullscreen mode
  • Why this is dangerous:
  • Sometimes the hook runs
  • Sometimes it doesn’t
  • The hook order breaks

React can no longer match state correctly


✅ Rule 2: Only call hooks from React functions

Hooks are allowed only inside:

  • Functional components
  • Custom hooks

❌ Not inside normal JavaScript functions.

Why? Because React only tracks hooks during render.


“This Code Works… So Why Is It Still Wrong?”

This confusion bothered me for a long time.

The Derived State Trap

I used to think something like this was okay:

const [isTop, setIsTop] = useState(imdbRating > 8);
Enter fullscreen mode Exit fullscreen mode

Then syncing it with an effect:

useEffect(() => {
  setIsTop(imdbRating > 8);
}, [imdbRating]);
Enter fullscreen mode Exit fullscreen mode

Technically:

  • No error
  • App works

But conceptually:

  • This is unnecessary state
  • This is bad design

The Correct Way of Thinking: Derive, Don’t Store

Here is the realization that changed my approach:

If:

  • A value can be fully derived from props or other state

Then:

  • It should not be stored as state

The better approach:

const isTop = imdbRating > 8;
Enter fullscreen mode Exit fullscreen mode

No extra state
No effect
No extra re-render

Cleaner logic and fewer bugs.


Understanding State Updates with Average Rating

Another important concept became clear with this pattern:

setAvgRating(Number(imdbRating));
setAvgRating((avg) => (avg + userRating) / 2);
Enter fullscreen mode Exit fullscreen mode

React supports:

  • Direct state updates
  • Functional updates

Functional updates should be used when:

  • The new state depends on the previous state

This avoids:

  • Stale values
  • Unexpected behavior due to batching

How I Now Visualize Hooks

This mental model helped me a lot:

  • Hooks = nodes in a linked list
  • React = the manager of that list
  • Changing the order = ❌ system breaks

That’s why:

  • Hooks must be called in the same order
  • On every render
  • Without conditions

Final Realization

The Rules of Hooks are:

  • Not arbitrary
  • Not React being “strict for no reason”

They are a direct result of React’s internal design.

Once I understood Fiber and hook ordering:

  • I didn’t need to memorize the rules
  • The rules started making logical sense

Final Thought

If you’re struggling with React Hooks:

  • Don’t just learn how to use them
  • Learn why they exist

Once the “why” is clear:

  • Code becomes cleaner
  • Bugs reduce
  • Confidence increases

This blog is written for future me as well —
so that this confusion never comes back again.

Top comments (0)