对数组使用 setState 时无限循环

Infinite Loop when using setState for array

我想在数组中存储 4 个“hi”。而不是:

strArr.push('hi');
strArr.push('hi');
strArr.push('hi');
strArr.push('hi');

我这样做了:

for(let i = 0; i<4; i++){
   setStrArr([...strArr, "hi"])
}

但是,我收到此错误:错误:重新呈现太多。 React 限制渲染次数以防止无限循环

我不知道哪里出了问题,我想知道它是否在 i=3 时没有到达。所以我做了一个检查:

for(let i = 0; i<4; i++){
   setStrArr([...strArr, "hi"])
   if(i==3){
      console.log("done")
   }
}

'i'的值确实达到了3,但为什么我的代码又运行了?

这是我的代码:

function MyApp(){
  const [strArr, setStrArr] = useState([]);
  for(let i = 0; i<4; i++){
       setStrArr([...strArr, "hi"])
       if(i==3){
          console.log("done")
       }
    }      
  return(
    <div>
    </div>
  )
}

我相信您可能遇到了问题,因为调用 setStringArray 是 'side effect'。

这将导致函数组件无限地 re-rendered,因为钩子已经更新了组件的状态,导致 re-render 然后无限地更新状态。

本质上,您不应该尝试在同一行中读取和更新 strArray

在完全填充数组后,您应该只调用 setStrArr 一次

我认为您还应该考虑使用 useEffect 挂钩来处理此类行为。

如果给定的参数(strArray 在这种情况下)发生变化,它传递的最后一个参数告诉 React 仅 运行 此 useEffect 钩子内部的代码。

这个钩子是为你想要在你的组件中有'side effects'而设计的。

所以也许你可以尝试类似的方法来防止你的无限 re-renders:

function MyApp(){
  const [strArr, setStrArr] = useState([]);

  useEffect(() => {
     const fourHiArray = [];

    for(let i = 0; i<4; i++){
       fourHiArray = ([...fourHiArray, "hi"])
       if(i==3){
          console.log("done")
       }
    } 

    setStrArr(fourHiArray);
  }, [strArr]);
       
  return(
    <div>
    </div>
  )
}

在重新渲染时会调用整个函数 MyApp,因此在每次重新渲染时,您的循环都会重新开始。如果只想调用一次,在useEffect中调用。像这样

useEffect(() => {*your code here*}, []}

使用空数组作为第二个参数的 useEffect 仅在第一次渲染时调用。

另外,如果您想设置初始状态,我建议您在 useState(['hi','hi','hi','hi'])

添加到其他答案中,因为您正在执行 同步 代码,所以我会使用 useLayoutEffect 挂钩。 useLayoutEffect 运行,React 等待它完成。这对于等待反应很好,因为代码都是同步的,并且它可以防止您的组件在屏幕上“闪烁”,因为渲染完成后会调用 useEffect。

function MyApp(){
  const [strArr, setStrArr] = useState([]);

  useLayoutEffect(() => {
     let fourHiArray = [];

    for(let i = 0; i<4; i++){
       fourHiArray = ([...fourHiArray, "hi"])
       if(i==3){
          console.log("done")
       }
    } 

    setStrArr(fourHiArray);
  }, []);
       
  return(
    <div>
    </div>
  )
}