React Rules of Hooks

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

React Rules of Hooks

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

Rules of React Hooks

There are two key rules of React Hooks:

  • Top-Level of Function Components: Only Call Hooks from the Top-Level of React Function Components
  • Fixed Calls: There must always be the same number of hook calls in a fixed order

Not following these rules will cause React to throw an error.

Top-Level of Function Components

Below we demonstrate what makes a valid Hook call as well as an invalid hook call based on the requirements of this rule.

App.tsx
import { useState } from "react";

/**
* Rule 1:
* Hook calls must be made from the top-level of a function component
*/

// ❌ This will fail because the hook call is not from a function component
// const [invalid, setInvalid] = useState('Jane');

function useCustom() {
// ✅ This is a valid hook call
// ∵ useCustom is called from the top-level of a function component
const [third, setThird] = useState("Jack");
return [third, setThird] as const;
}

export default function App() {
// ✅ This is a valid hook call
// ∵ we are at the top-level of the App function component
const [first, setFirst] = useState("Jane");
const [second, setSecond] = useState("John");
const [third, setThird] = useCustom();

return (
<div>
<input value={first} onChange={(e) => setFirst(e.target.value)} />
<input value={second} onChange={(e) => setSecond(e.target.value)} />
<input value={third} onChange={(e) => setThird(e.target.value)} />
</div>
);
}

Fixed Calls

You cannot have hook calls dynamically e.g. you cannot call them conditionally.

App.tsx
import { useState } from "react";

/**
* Rule 2:
* There must always be the same number of hook calls in a fixed order
*/

export default function App() {
// ✅ This is a valid hook call
// ∵ they will always be called in the same order when the component renders
const [first, setFirst] = useState("Jane");
const [second, setSecond] = useState("John");

let third: string | undefined;
let setThird: (value: string) => void;
if (first === second) {
// ❌ This will fail because the hook may or may not be called
// [third, setThird] = useState('Doe');
}

return (
<div>
<input value={first} onChange={(e) => setFirst(e.target.value)} />
<input value={second} onChange={(e) => setSecond(e.target.value)} />
{!!third && (
<input value={third} onChange={(e) => setThird(e.target.value)} />
)}
</div>
);
}

ESLint Plugin

You can use the ESLint Plugin for React Hooks to catch violations of Rules of Hooks during development / application build.

javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

We've looked at the use state hook and react, and we've even looked at building our own custom hooks by wrapping this particular hook. But before we look at additional hooks that come built in with React, we need to cover some restrictions which we must follow when working with hooks and react. These other rules of hooks, now there are only really two rules to hooks and we can demonstrate them with a simple example containing two state variables. We wired these two state variables to two simple input elements to build a very simple application. Now there should be nothing surprising

00:32

for this application at this point. We can actually demo it within the UI as well. We have the two states, the two inputs, and they work exactly how you would expect. The first rule of hooks is pretty simple. Hooks must be called from function components. So if you were to take our first use state and move it outside of the app function, this is valid. Our script, as you can see, there are no compiled time errors. However, this is not a valid use of a hook. Hook calls must be from within a function component. And actually if we jump to the running application,

01:04

you can see that it has crashed within the developer console. We can see the warning getting logged, that there is an invalid hook call. Hook Calls must be made from within a function component and the fix is pretty simple. We need to make sure that the call is made from within the function body. And the reason for this is actually pretty simple. Hooks are associated with a particular component and it doesn't make sense to invoke them from outside of the component. Now we've already looked at custom hooks and as you might recall, we create them by calling other hooks from within our custom hook.

01:37

So you must be wondering, doesn't that break this rule? Well actually, not to prove that. Let's do a simple refactoring, take our two use states and move it into a higher level custom hook that manages both of these for us. Now the custom hook of course, must also be invoked from the function component. Now even though these two calls exist outside of the app, as far as react is concerned, it'll see these calls being made from within the app and therefore we are not breaking this particular rule. So custom hooks are nothing more than a fancy way to move out repeated code.

02:11

As far as react is concerned, it is still being called from the function component and the fact that there is an internal call to another custom function is going to be opaque for react. The second rule of hoax is a bit more restrictive. They must always be the same number of calls in the same order in your function components. To demonstrate that let's create a third state variable, but this time we do it conditionally only. If the first and the second values are exactly the same, do we create a third state? And of course we will then conditionally try to render a third input element.

02:45

Now of course, again, this is perfectly valid JavaScript, but it violates the rules for hooks. So let's jump into the UI and demo what will happen if we try to run this code. Now the third state call is only made If the first two state values are the same. So let's modify the second one to be Jane as well. And you can see that the application has crashed as soon as we try to conditionally make another call to a U State. And we can see on the developer console that React is pointing out the reason why the application has crashed. React has detected a change in the order

03:18

of the hooks called by the app. And fundamentally in the previous render there was not a third call and in this render there was a third call. Basically the number of calls between the different renders need to be the same. Now in addition to conditional rendering, the same issue would happen of course if you were using a loop because we could have different number of items between the different renders. Now let's try and understand why reactant forces these restrictions. And this will actually give you an insight into the internals of how React hooks work as well. Basically, react tracks hooks while it is

03:51

rendering a component. So of course if you make a call outside of a component, it's going to throw an error. Now to understand the reason for the second rule, we need to understand how React is going to track these calls. And the answer is deceptively simple. It simply tracks them by index. So it notes down that the first call has been made and it has been initialized with the string Jane. And then a second call is made initialized to John and a third call initialized to Alex. So for these three simple hook calls, react simply stores them against a particular index. Now, if you were to conditionally not make a call

04:24

to the second hook, they would be nothing telling react which particular index disappeared. As far as it is concerned, the second call should now be John and this would be very, very bad. Fortunately, react does track how many calls are made and if more or lesser calls are made, react will throw an error at runtime instead of giving us some weird behaviors. Now, even though these are only two simple rules, I understand that it might sound scary to follow them reliably, but fortunately they can be caught easily at compile time

04:56

by Es Clint, and that is something that is probably configured on any React template that you pick up, including the one that we are using. I actually disabled it to demo the runtime errors. So let's just enable it to again to see how these issues get caught at compile time. Here we are doing everything wrong. We are calling hooks outside of function components and we are calling hooks conditionally. And of course, while running our application is not having a good time. What would be great if these errors are caught at will time? And for that we have Elint right now to demo the application of failing at runtime.

05:29

I have the React app rules disabled, but let's just enable them. And now you can see that both of these issues have been identified by ES l while compiling. So in summary, now you know the two rules. So folks, and you don't have to worry about them too much because they will most likely be caught at bill time. For you per I'll wrap things up there. As always, thank you for joining me and I will see you in the next one.