Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need a clearer explanation how to avoid infinite re-rendering with React hooks

Not so fluent with React hooks, used plenty of class components before, hope you'll be forgiving.

The current code causes infinite re-rendering, and I think I understand why - the entire function body is being called on re-render.

const NavTabs = () => {
  const classes = useStyles();

  const [categories, setCategories] = React.useState();
  const axiosPromise = getRequest(consts.categoriesURL);

  axiosPromise.then(data => {
    setCategories(data.value);
  })

  return (
    <div className={classes.root}>
      <AppBar position="static">
      </AppBar>
      {categories && <DynamicTabs categories={categories}/>}
    </div>
  );
}

I guess I could do something like if (!categories) { const axiosPromise [...] and so forth, i.e. do the http request only if categories haven't been populated yet. I guess this could also be solved by useEffect? Or wrapping the hook in an internal function?

I guess my real question is - why is React re-rendering the entire function body? Shouldn't it re-render only the return function? And then what is the point of using hooks that will be re-run on every render?

Compared to class components - shouldn't the code in the function body be equivalent to the constructor code in class components, and the return function - equivalent to the render method?

like image 848
Gilad Barner Avatar asked Oct 18 '25 19:10

Gilad Barner


2 Answers

I guess I could do something like if (!categories) { const axiosPromise [...] and so forth, i.e. do the http request only if categories haven't been populated yet. I guess this could also be solved by useEffect? Or wrapping the hook in an internal function?

Yes, useEffect is the way to go here. Making a request and setting the result as state are side effects should only be run once in your case. We can achieve that easily with useEffect.

I guess my real question is - why is React re-rendering the entire function body? Shouldn't it re-render only the return function? And then what is the point of using hooks that will be re-run on every render?

React has no way to split a js function and only re-render the return. The function is atomic and must be completed. That is what hooks are for. React controls when hooks are ran so it can do fun stuff like batch state updates, ignore outdated effects and prioritise high priority work like animations.

Compared to class components - shouldn't the code in the function body be equivalent to the constructor code in class components, and the return function - equivalent to the render method?

The functional component is equivalent to the render method of a class component. They are called in a similar way. All the other lifecycle methods are replaced by hooks.

I recommend the react docs are great place to start and Dan Abramov has a great deep dive on hooks.

like image 52
Will Avatar answered Oct 21 '25 11:10

Will


Yes, getRequest is being invoked each render cycle which sets some state and triggers a rerender. Placing it in an effect hook with a dependency array is likely the best solution. What dependencies you define will dictate when getRequest can be invoked.

Why is React re-rendering the entire function body?

The entire function body needs to run in order to determine the return value.

And then what is the point of using hooks that will be re-run on every render?

Hooks are run on every render, in the same order they are defined, but depending on dependencies may not invoke a callback. Hooks are what give functional components so much viability and sense of component lifecycle, to nearly be equivalent to class-based components in functionality. In most cases, you can completely convert a class-based component to a functional one and not drop any functionality.

Compared to class components - shouldn't the code in the function body be equivalent to the constructor code in class components, and the return function - equivalent to the render method?

It is more accurate to think of the entire functional components definition as the class-based render function, which can contain some logic and returns computed JSX to render to the DOM.

Example Solution:

const NavTabs = () => {
  const classes = useStyles();

  const [categories, setCategories] = React.useState(); // <-- no initial state!

  useEffect(() => {
    getRequest(consts.categoriesURL).then(data => {
      setCategories(data.value); // <-- will update state and trigger render
    });
  }, []); // <-- empty dependency is run once on component mount

  return (
    <div className={classes.root}>
      <AppBar position="static">
      </AppBar>
      {categories && <DynamicTabs categories={categories}/>}
    </div>
  );
}
like image 35
Drew Reese Avatar answered Oct 21 '25 12:10

Drew Reese



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!