The First Rule of React Hooks, In Plain English

Why hooks must always be called at the top level of your components

Preface

So you’ve just learned how to use React Hooks. Hopefully you’ve read the official documentation on the Rules of Hooks so you know how to avoid some of the common pitfalls that may break your code. You’ve also hopefully configured ESLint to enforce these rules throughout your code.

The first rule of React Hooks is “only call hooks at the top level”. This means that within a given component, its hooks should never be called in such a way that the order in which those hooks are called might ever be different between different renders. Read that slowly, twice. Hooks should not be placed inside loops, conditions, or nested functions. By placing all of your hooks at the top-level, React can be sure that a given component’s hooks will always be called in the same order.

At first glance this rule may seem unintuitive. Why should they need to be called in the same order every render? Understanding this rule requires you to step into React’s shoes.

Explanation

Take a look at a simple example with two hook calls.

Suppose we render this component. React is keeping track of two pieces of state for us, and needs a way to know what to return for each useState call when this component is re-rendered. The only sensible thing for React to do here is store these two pieces of state in an array so that when the component is re-rendered it can know “the first call to useState is associated with the data at index 0 of the array and the second call to useState is associated with the data at index 1 of the array”.

A simplified representation of how React stores the data for our two hooks.

To be extra explicit, React sees the first useState call in the initial render, looks at the first element of its hooks array for that component, sees nothing, and then sticks the initial value you gave it (“Game of Thrones”) into the first element of the array. It then does the same thing for the second call to useState but with the second element of the array, and so on and so forth for any other hooks calls that might be in the component.

This is why it’s crucial that the hooks are called in the same order every time the component renders. React maps the order of hook calls to array elements to know which data is associated to which hook call in a given render. Here’s an example where we break this rule.

Notice there are now four hooks being used. After the initial render, React’s internal array looks like this:

Suppose now that the title was set to an empty string, triggering a re-render of the component. Keep in mind that React still has that 4-element array in its memory. Since our if condition will evaluate to false , React will associate the first useState call to the first element of the array correctly, but will then associate the second useState call to the second element of the array (since it’s the second hook call made in this specific render), which in this case is a function that should be associated to a useEffect hook call. As you can see, this effect will cascade down through the array and cause all manner of bugs.

To fix this specific issue, simply move the condition into the body of the function passed to the useEffect call.

Before 👎

After 👍

Conclusion

So this is why hooks must always be called at the top level of a component. It’s the only way React can be sure that each hook will be called in exactly the same order each time the component is rendered. The best way to enforce this rule is to install the ESLint plugin I mentioned at the top of this article!

Thoughts? Let me know in the comments!

Happy coding! 🤙