Error when setting values a loop "React Limits the number of renders to Prevent an Infinite loop"

Asked

Viewed 490 times

-1

Well, I have a useEffect that searches my data from an API and then set the values to a useState, but it’s entering an infinite loop and I can’t get my values in the other variable I created.

export default function ForecastDashboard() {
    const [codCity, setCodCity] = useState(4209102);

    const [dataCity, setDataCity] = useState([]);
    const [dataClimate, setDataClimate] = useState([]);

    useEffect(() => {
        try {
            
            const getAllDataClimate = async () => {
                const { data } = await apiClima.get(`/${codCity}`);
                const cities =  data[codCity];
                setDataCity(cities);     
            };

            getAllDataClimate();

        } catch (e) {
            console.error(e);
        }       
    }, []);

    

    for(const i in dataCity){
        const { manha, tarde, noite } = dataCity[i];
        console.log(manha, tarde, noite)
        //setDataClimate([ manha, tarde, noite ]);
    }

    return (
        <View style={ForecastDashboardCSS.container}>
            {dataClimate.map(i => (
                <View style={ForecastDashboardCSS.card}>
                    <Text>{i}</Text>
                </View>
            ))}
        </View>
    );
}

Data I’m receiving from the variables of manha, tarde and noite.

I’m a little lost because I don’t know exactly what I have to do to get around it. I was looking for some solutions, but I was not successful. My mistake occurs if I put the setDataClimate within the for.

1 answer

1


The fact that you enter an infinite loop is due to the fact that you call the setDataClimate in the loop for within the body of the function, you shouldn’t do this!. The React documentation itself explains:

Mutations, signatures, timers, logs and other side effects are not allowed within the main body of a functional component (React refers to this as render Phase). Using them will lead to confusing errors and inconsistencies in the UI.

One of these errors is infinite loop. The fact that you render a component based on a state variable (dataClimate) and make a setState of that variable immediately within the body of the function (setDataClimate), may be the reason for this error.

And more:

Instead of that, use useEffect. The function passed to useEffect will be executed after the rendering is available on the screen. Think of effects as an escape route from React’s purely functional world to the imperative world.

So to solve your case, let’s use an effect hook for the property dataCity.

I made a mock of your code to recreate your problem. Below your code mockery structured to simulate your error:

function App() {
  const mock = { '4209102': [{ cidade: 'cidade1', manha: 'ceu claro', tarde: 'chuvoso', noite: 'nublado' }, { cidade: 'cidade2', manha: 'ceu claro', tarde: 'chuvoso', noite: 'nublado' }, { cidade: 'cidade3', manha: 'ceu claro', tarde: 'chuvoso', noite: 'nublado' }] };

  const [codCity, setCodCity] = React.useState(4209102);
  const [dataCity, setDataCity] = React.useState([]);
  const [dataClimate, setDataClimate] = React.useState([]);

  React.useEffect(() => {
    try {
      const getAllDataClimate = () => {
        // const { data } = await apiClima.get(`/${codCity}`);
        const { data } = { data: mock };
        const cities = data[codCity];

        setDataCity(cities);
      };

      getAllDataClimate();

    } catch (e) {
      console.error(e);
    }
  }, []);

  
  for (const i in dataCity) {
   const { manha, tarde, noite } = dataCity[i];
   console.log('LOGS: ', manha, tarde, noite);
   setDataClimate([manha, tarde, noite]);
  }

  return (
    <div>
      <h1>Teste</h1>
      {dataClimate.map((value, index) => (
        <div key={index}>
          <p>{value}</p>
        </div>
      ))}
    </div>
  );
}

ReactDOM.render( <App /> , document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>

Now we will adapt the code to solve the problem. We will use the useEffect to the property dataCity. What’s this hook gonna look like:

  React.useEffect(() => {
    for (const i in dataCity) {
      const { manha, tarde, noite } = dataCity[i];
      console.log('LOGS: ', manha, tarde, noite);
      setDataClimate([manha, tarde, noite]);
    }
  }, [dataCity]);

Now, when dataCity has the status changed by getAllDataClimate, we will work upon this change through the useEffect and reflect this change using setDataClimate for ai yes the components can be rendered without error.

Code mockery complete and running below:

function App() {
  const mock = { '4209102': [{ cidade: 'cidade1', manha: 'ceu claro', tarde: 'chuvoso', noite: 'nublado' }, { cidade: 'cidade2', manha: 'ceu claro', tarde: 'chuvoso', noite: 'nublado' }, { cidade: 'cidade3', manha: 'ceu claro', tarde: 'chuvoso', noite: 'nublado' }] };

  const [codCity, setCodCity] = React.useState(4209102);
  const [dataCity, setDataCity] = React.useState([]);
  const [dataClimate, setDataClimate] = React.useState([]);

  React.useEffect(() => {
    try {
      const getAllDataClimate = () => {
        // const { data } = await apiClima.get(`/${codCity}`);
        const { data } = { data: mock };
        const cities = data[codCity];

        setDataCity(cities);
      };

      getAllDataClimate();

    } catch (e) {
      console.error(e);
    }
  }, []);

  React.useEffect(() => {
   for (const i in dataCity) {
    const { manha, tarde, noite } = dataCity[i];
    console.log('LOGS: ', manha, tarde, noite);
    setDataClimate([manha, tarde, noite]);
   }
  }, [dataCity]);

  return (
    <div>
      <h1>Teste</h1>
      {dataClimate.map((value, index) => (
        <div key={index}>
          <p>{value}</p>
        </div>
      ))}
    </div>
  );
}

ReactDOM.render( <App /> , document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>

The thing about passing the dataCity as a parameter in useEffect is explained in the documentation:

However, this may be excessive in some cases, such as the signature example from the previous section. We don’t need to create a new subscription every time we update, only if the props source is changed.

To implement this, pass a second argument to useEffect which can be an array of values in which the effect observes.

In this case, we are observing dataCity.


Heed

This code uses React Web only to illustrate your problem and the solution in a way that can be tested directly here on Sopt thanks to the code snippet. Make the appropriate adaptations to make the code work in React Native. Replace div for View, p for Text, ....

  • I tested it here and it worked, thanks. I didn’t know I couldn’t put a code pad like that

Browser other questions tagged

You are not signed in. Login or sign up in order to post.