如何停止`setState`中的无限循环

huangapple go评论62阅读模式
英文:

How to stop infinite loop in setState

问题

我遇到的问题是,调用setState()在我的React组件中引发了无限循环。具体来说,我正在调用API以获取一张猫的图片,并将图像URL推送到一个名为arr的数组中。然后,我尝试通过调用setImages(arr)来更新组件的状态,并将图像变量记录到控制台。

然而,调用setImages()会触发组件的重新渲染,这会导致for循环再次执行,导致另一个API调用,另一个推送到arr,并再次调用setImages(),从而导致无限循环。

我期望状态变量images具有3个图像URL,因为for循环被迭代三次。

英文:
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.

答案1

得分: 4

你需要将你的fetch操作包裹在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;
}





英文:

You need to wrap your fetch operations in a useEffect:

import { useState, useEffect } from &quot;react&quot;;

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

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(() =&gt; {
    Promise.all(Array.from({ length: 3 }, getCat)).then(setPics);
  }, []);

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

  return (
    &lt;div className=&quot;app&quot;&gt;
      {pics.map((url) =&gt; (
        &lt;img key={url} src={url} alt=&quot;A kitten&quot; /&gt;
      ))}
    &lt;/div&gt;
  );
}

<!-- begin snippet: js hide: true console: true babel: false -->

<!-- language: lang-css -->

.app {
  display: flex;
  flex-direction: column;
}

<!-- language: lang-html -->

&lt;script type=&quot;text/babel&quot;&gt;
const { useState, useEffect } = React;

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

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(() =&gt; {
    Promise.all(Array.from({ length: 3 }, getCat)).then(setPics);
  }, []);

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

  return (
    &lt;div className=&quot;app&quot;&gt;
      {pics.map((url) =&gt; (
        &lt;img key={url} src={url} alt=&quot;A kitten&quot; /&gt;
      ))}
    &lt;/div&gt;
  );
}

ReactDOM.createRoot(document.querySelector(&quot;#root&quot;)).render(&lt;App /&gt;);
&lt;/script&gt;

&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
&lt;script src=&quot;https://unpkg.com/@babel/standalone@7/babel.min.js&quot;&gt;&lt;/script&gt;
&lt;script crossorigin src=&quot;https://unpkg.com/react@18/umd/react.development.js&quot;&gt;&lt;/script&gt;
&lt;script crossorigin src=&quot;https://unpkg.com/react-dom@18/umd/react-dom.development.js&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

答案2

得分: 1

以下是代码的翻译部分:

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>
  
}
英文:

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 = () =&gt; {


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

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

    console.log(&#39;Images: &#39;+images)

  return &lt;div&gt;The Rest of your component here&lt;/div&gt;
  
}

huangapple
  • 本文由 发表于 2023年3月31日 22:13:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75899556.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定