Бесконечный цикл при использовании setState для массива

Я хотел хранить в массиве 4 привет. Вместо того:

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>
  )
}

person jason1234    schedule 30.10.2020    source источник
comment
Где в JSX вы делаете это для цикла? Вы пользуетесь крючками? какова зависимость хука?   -  person Amir-Mousavi    schedule 30.10.2020
comment
@ Amir-Mousavi добавил мой код   -  person jason1234    schedule 30.10.2020


Ответы (3)


Я считаю, что у вас могут быть проблемы, потому что вызов setStringArray - это «побочный эффект».

Это приведет к бесконечному повторному рендерингу функционального компонента из-за того, что ловушка обновила состояние компонента, вызывая повторный рендеринг, который затем обновляет состояние и так и бесконечно.

По сути, вы не должны пытаться одновременно читать и обновлять strArray в одной строке.

Вам следует просто вызвать setStrArr один раз только после того, как вы полностью заполнили свой массив.

Я думаю, вам также следует изучить возможность использования ловушки useEffect для подобного поведения.

Последний передаваемый параметр указывает React запускать код внутри этого useEffect хука только в том случае, если заданный параметр (strArray в данном случае) изменяется.

Этот хук разработан, когда вы хотите иметь "побочные эффекты" в вашем компоненте.

Так что, возможно, вы могли бы попробовать что-то вроде предотвращения бесконечных повторных рендеров:

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>
  )
}
person Erin Eland    schedule 30.10.2020
comment
Спасибо. Но не могли бы вы объяснить мне, почему изменение состояния вызывает повторный рендеринг? Все это время я думал, что только использование хуков типа useEffect приведет к бесконечному повторному рендерингу. - person jason1234; 31.10.2020
comment
Не стоит беспокоиться! Надеюсь это поможет. Таким образом, компонент React будет всегда повторно отрисовывать при обновлении состояния! Это случай «так работает React». Здесь есть классная запись в блоге: lucybain.com/blog/2017/react-js-when-to-rerender/. - person Erin Eland; 10.11.2020

При повторной визуализации вызывается вся функция MyApp, таким образом, при каждой повторной визуализации ваш цикл запускается заново. Если вы хотите вызвать его только один раз, вызовите его в useEffect. Вот так

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

useEffect с пустым массивом, поскольку второй параметр вызывается только при первом рендеринге.

также, если вы хотите установить свое начальное состояние, я бы рекомендовал сделать это в useState (['привет', 'привет', 'привет', 'привет'])

person P. Soltes    schedule 30.10.2020

Добавляя к другим ответам, поскольку вы выполняете синхронный код, я бы использовал ловушку 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>
  )
}
person Will    schedule 30.10.2020
comment
Спасибо большое. Я еще не использовал useLayoutEffect раньше, прочту - person jason1234; 31.10.2020