skip to Main Content

This code below produces an infinite loop, maxing out the rendering.

I’m unsure why this is happening.

We have a similar problem in our enterprise codebase that has been resolved by changing the constant array to a state variable so it doesn’t always change and by removing it from the useEffect because it won’t change.

I’m guessing that by setting y, a different variable, to a constant array, it causes a rerender and x changes because the pointer value changes?

I’m not sure but I find this behavior odd.

import React, { Component, useState, useEffect, useCallback } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';

const App = (props) => {
  const [y, setY] = useState([]);
  const x = [1001, 1002];

  setY(x);

  useEffect(() => {
    console.log(x);
  }, [x]);

  return <div></div>;
};

render(<App appName="User Details" />, document.getElementById('root'));

2

Answers


  1. The infinite loop in your code is caused by incorrect usage of the useState hook and the setY function.

    In React, the useState hook returns an array with two elements: the current state value and a function to update that state value. When you call setY(x), you’re trying to update the state value of y to x. However, since x is a constant array [1001, 1002], it cannot be updated. Instead, you should initialize y with the initial value of x:

    const [y, setY] = useState(x);
    

    Additionally, in your useEffect hook, you have specified x as a dependency. This means that the effect will run whenever x changes. However, since x is a constant array that doesn’t change, the effect will run indefinitely, causing the infinite loop.

    To fix this, you can remove x from the dependency array, or if you want to log the initial value of x once, you can pass an empty dependency array ([]) to ensure the effect runs only once when the component mounts:

    useEffect(() => {
      console.log(x);
    }, []);
    

    Here’s the updated code:

    import React, { useState, useEffect } from 'react';
    import { render } from 'react-dom';
    
    const App = () => {
      const x = [1001, 1002];
      const [y, setY] = useState(x);
    
      useEffect(() => {
        console.log(x);
      }, []);
    
      return <div></div>;
    };
    
    render(<App />, document.getElementById('root'));
    

    With these changes, the code should no longer result in an infinite loop and excessive rendering.

    Login or Signup to reply.
  2. The truth is exactly the opposite from what @Anubhav Raj suggests.

    Each time your component renders, x is an entirely new array by reference. And you are calling setY with it without any kind of wrapper (usually you’ll call these setters in useEffect or a callback you create with useCallback, but in this case it doesn’t matter because it will always happen on every render anyway).

    Think of it this way:

    let x = [1, 2];
    x = [1, 2];
    

    These are two different Array objects, even though the elements of both are the same. The fact that this is happening in two different runs of the same function makes it difficult to reason about, but essentially the same thing is happening.

    Because you’re calling setY with that new value, that queues up a rerender, causing the whole process to happen again.

    Check out this post for more info.

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