skip to Main Content

If I use the following code:

App.js:

import useFoo from "./useFoo";
import "./styles.css";

export default function App() {
  let foo = 3;

  if (Math.random() > 0.5) {
    foo = useFoo(6);
  }

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      {foo}
    </div>
  );
}

where useFoo() is merely to return the square of a number:

useFoo.js

export default function useFoo(a) {
  return a * a;
}

This can be shown on https://codesandbox.io/s/jolly-knuth-jwshws?file=/src/App.js

So:

  1. if you keep on refreshing the resulting page, you do see a 3 and 36 at random, so it does seem like "a React Hook", in this case useFoo() doesn’t need to be at top level

  2. at work, I actually encountered a pattern which is

const [bar, useBar] = useBar(value1, value2);

and even thought it looks very similar to a useState(), I found that useBar() merely works like a function like useFoo() above. That is, it doesn’t have the "keeping a state and bar will stay the same" kind of magic like useState(). (but on second thought, even useState() in fact gives a value back and assign it to the value in const [value, setValue] = useState(value1), so there is real assignment going on, but just that useState() chooses to return the same value if no useBar() is called).

But the question is:

  1. If I use the same thing:
let [bar, setBar];

if (true) {
  [bar, setBar] = useBar(value1, value2);
}

then the Visual Studio Code with error and warning messages built in will complain it is a bug: that React Hooks must be at the top level. However, if I run the webapp, it still runs as normal. So the question is:

Do all React Hooks need to run at the top level? If they do, then why does useBar() above work?

If only some React Hooks need to be at top level, then what determines that they must be at top level? Is it just by linting really? (so if you ignore the linting, the program will run into unexpected results? I suppose it will give a lint error instead lint warning, so that on the GitHub review, your coworker won’t approve it. But then, the useBar() above can work despite the lint error, if it exists. So what determines whether it must be top level or not?

(that is, I found that sometimes the Hooks or Custom Hooks are merely like a function. And if it is function, it really doesn’t have the rule of needing to be at the top level).

2

Answers


  1. React Hooks are functions that let you "hook into" React state and lifecycle features from function components. They allow you to use state and other React features without writing a class component. However, React Hooks must be called at the top level of a function component, before any early returns.

    React relies on the order in which Hooks are called to maintain state and effects. Calling Hooks conditionally or inside loops can disrupt this order and lead to unexpected behavior. For example, if you call a Hook conditionally, React may not be able to track which Hooks have been called, and this can lead to errors.

    In addition, calling Hooks inside loops can lead to performance problems. React will need to re-render the component every time the loop iterates, and this can be slow.

    Therefore, it is best practice to always call React Hooks at the top level of a function component. This will help to ensure that your code is consistent and predictable.

    function App() {
      const [foo, setFoo] = useState(3);
    
      if (Math.random() > 0.5) {
        foo = useFoo(6);
      }
    
      return (
        <div>
          <h1>Hello CodeSandbox</h1>
          <h2>Start editing to see some magic happen!</h2>
          {foo}
        </div>
      );
    }
    
    Login or Signup to reply.
  2. Because having a predictable sequence is important.

    Don’t call Hooks inside loops, conditions, or nested functions.
    Instead, always use Hooks at the top level of your React function,
    before any early returns. By following this rule, you ensure that
    Hooks are called in the same order each time a component renders.
    That’s what allows React to correctly preserve the state of Hooks
    between multiple useState and useEffect calls.

    Source

    And

    Instead, to enable their concise syntax, Hooks rely on a stable call
    order on every render of the same component. This works well in
    practice because if you follow the rule above (“only call Hooks at the
    top level”), Hooks will always be called in the same order.

    Source

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search