drippy frippy
drippy frippy

Reputation: 19

How to stop infinite loop in setState

const cats = [
    1, 2, 3
  ];
  let arr = [];
  const [images, setImages] = useState([]);

  const generateCat = () => {
    fetch(
      "https://api.thecatapi.com/v1/images/search?size=small&limit=1&mime_types=jpg,png&width=200&height=200"
    )
    .then(response=>{return response.json()})
    .then(data=>{
      let finalResult = data[0]['url']
      arr.push(finalResult)
      console.log(arr)
      return finalResult
    })
  };

  for(let i=0;i<cats.length;i++){
    generateCat()
    setImages(arr)
    console.log('Images: '+images)
  }

My problem is that I'm encountering an issue with setState() causing an infinite loop in my React component. Specifically, I'm making an API call to retrieve an image of a cat and pushing the image URL to an array called arr. Then, I'm attempting to update the state of the component by calling setImages(arr) and logging the images variable to the console.

However, calling setImages() triggers a re-render of the component, which causes the for loop to execute again, leading to another API call, another push to arr, and another call to setImages(), resulting in an infinite loop.

I am expecting the state variable images to have 3 img urls since the for loop is being iterated thrice.

Upvotes: 0

Views: 73

Answers (2)

moonwave99
moonwave99

Reputation: 22797

You need to wrap your fetch operations in a useEffect:

import { useState, useEffect } from "react";

const API_URL =
  "https://api.thecatapi.com/v1/images/search?size=small&limit=1&mime_types=jpg,png&width=200&height=200";

async function getCat() {
  const response = await fetch(API_URL);
  const data = await response.json();
  return data[0].url;
}

export default function App() {
  const [pics, setPics] = useState([]);
  useEffect(() => {
    Promise.all(Array.from({ length: 3 }, getCat)).then(setPics);
  }, []);

  if (!pics.length) {
    return "Loading...";
  }

  return (
    <div className="app">
      {pics.map((url) => (
        <img key={url} src={url} alt="A kitten" />
      ))}
    </div>
  );
}

.app {
  display: flex;
  flex-direction: column;
}
<script type="text/babel">
const { useState, useEffect } = React;

const API_URL =
  "https://api.thecatapi.com/v1/images/search?size=small&limit=1&mime_types=jpg,png&width=200&height=200";

async function getCat() {
  const response = await fetch(API_URL);
  const data = await response.json();
  return data[0].url;
}

function App() {
  const [pics, setPics] = useState([]);
  useEffect(() => {
    Promise.all(Array.from({ length: 3 }, getCat)).then(setPics);
  }, []);

  if (!pics.length) {
    return "Loading...";
  }

  return (
    <div className="app">
      {pics.map((url) => (
        <img key={url} src={url} alt="A kitten" />
      ))}
    </div>
  );
}

ReactDOM.createRoot(document.querySelector("#root")).render(<App />);
</script>

<div id="root"></div>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

Upvotes: 4

Alex Rabin
Alex Rabin

Reputation: 422

I am assuming this code is inside of a react component so here is modified example that should prevent the pre-rendering:


const cats = [
    1, 2, 3
  ];

const ExampleComponent = () => {


  const [images, setImages] = useState([]);
  
  // this code will only be called on initial load of the component
  useEffect(() => {
    const generateCat = () => {
    fetch(
      "https://api.thecatapi.com/v1/images/search?size=small&limit=1&mime_types=jpg,png&width=200&height=200"
    )
    .then(response=>{return response.json()})
    .then(data=>{
      let finalResult = data[0]['url']
      
      setImages(prev => [...prev, finalResult])
    })
  };

  for(let i=0;i<cats.length;i++){
    generateCat()
  }
  },[])

    console.log('Images: '+images)

  return <div>The Rest of your component here</div>
  
}

Upvotes: 1

Related Questions