4

I wanted to store 4 "hi" in an array. Instead of:

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

I did this:

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

However, I get this error: Error: Too many re-renders. React limits the number of renders to prevent an infinite loop

I couldn't figure out what was wrong and I was wondering if it didn't reach when i=3. So I did a check:

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

The value of 'i' did reach 3, but why does my code run again?

enter image description here

This is my code:

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>
  )
}
jason1234
  • 201
  • 5
  • 14

3 Answers3

2

I believe you may be experiencing issues because calling setStringArray is a 'side effect'.

This will cause the function component to be re-rendered infinitely, due to the hook having updated the component's state, causing a re-render which then updates the state and on and on infinitely.

Essentially, you shouldn't be trying to both read and update strArray in the same line.

You should simply only call the setStrArr once after having fully populated your array.

I think you should also look into using the useEffect hook for behaviour like this.

The last parameter it get's passed tells React to only run the code inside of this useEffect hook if the paramater given (strArray in this case) changes.

This hook is designed for when you want to have 'side effects' in your component.

So perhaps you could try something like to prevent your infinite 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>
  )
}
heartunicyclist
  • 313
  • 1
  • 3
  • 15
  • 1
    thank you. But could you please explain to me further why would changing the state causes re-rendering? All along I thought only the usage of hooks like `useEffect` would cause things to re-rendering infinitely. – jason1234 Oct 31 '20 at 01:37
  • No worries! Hope this helps. So a React component will *always re-render* when the state is updated! This is a case of 'it's just how React works'. There's a cool blog post about it here: https://lucybain.com/blog/2017/react-js-when-to-rerender/#:~:text=The%20component's%20state%20changes,should%20re%2Drender%20the%20component. – heartunicyclist Nov 10 '20 at 13:54
1

On rerender whole function MyApp is called, thus on every rerender your cycle is started anew. If you only want to call it once call it in useEffect. Like so

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

useEffect with empty array as second param is only called on first render.

also if you want to set your inital state i would recomend doing it in useState(['hi','hi','hi','hi'])

P. Soltes
  • 38
  • 7
0

Adding on to the other answers, since you are executing synchronous code I would the use the useLayoutEffect hook. useLayoutEffect runs, and React waits for it to finish. This is fine for react to wait since the code is all synchronous and it prevents your component from "blinking" on screen because useEffect gets called after render is done.

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>
  )
}
Will
  • 309
  • 1
  • 4
  • 13