Сколько раз React должен отображаться?

#javascript #reactjs #rendering

Вопрос:

Я написал простой React, чтобы проверить, как React рендерится

 import ReactDOM from 'react-dom';
import React, { useState, useEffect } from 'react';

const App = (props) => {
  // State
  const [trackIndex, setTrackIndex] = useState(1);
  const [trackProgress, setTrackProgress] = useState(0);
  const clickHandler = () =>{
       setTrackIndex((trackIndex)=>trackIndex 1)
  }

  useEffect(() => {
    console.log("effect; trackIndex=",trackIndex)
    console.log("effect; trackProgress=",trackProgress)
    if(trackIndex<5){
      console.log("set trackProgress")
      setTrackProgress(trackIndex)
    }
  });

  console.log("render")

  return (
    <div>
      <p>{trackIndex}</p>
      <p>{trackProgress}</p>
      <button onClick={clickHandler}>Click me</button>
    </div>
  );
};

ReactDOM.render(
  <App />,
document.getElementById('root')
);
 

Ниже приведен вывод консоли:

 render
effect; trackIndex= 1
effect; trackProgress= 0
set trackProgress
render
effect; trackIndex= 1
effect; trackProgress= 1
set trackProgress
render
 

Кажется, что React отображается три раза, прежде чем я нажму кнопку. Последний рендеринг меня действительно смущает. Может ли кто-нибудь объяснить мне, почему происходит этот рендеринг и почему после этого рендеринга не выполняется никакого эффекта? Заранее благодарю

Ответ №1:

Кажется, что вы useEffect устанавливаете состояние с помощью параметра setTrackProgress on при первоначальном рендеринге компонента. Это потому trackIndex , что начинается с 1 того, что меньше 5. (видно по if(trackIndex<5) ) Обратите внимание, что вы не указали зависимость от массива в useEffect качестве второго параметра. Согласно react-documentation это означает, что эффект будет возникать только один раз после первого начального рендеринга компонента.

Кроме того, я бы предложил добавить useCallback к вашему clickHandler , чтобы предотвратить переопределение функции для каждого рендеринга. (Согласно react-documentation)

Ответ №2:

Вы практически создаете бесконечный цикл, но React спасает вас. Первый render журнал — это начальный рендеринг, затем useEffect выполняется, и ваш компонент повторно отображает, что приводит ко 2-му render журналу и первому effect; ... журналу. Теперь useEffect выполняется 2-й. Однако значение trackIndex не изменилось, поэтому ваш оператор if будет оценивать true и обновляться trackProgress с тем же состоянием / значением (1). Это приводит к еще одному повторному отображению и третьему render журналу. Теперь вы могли бы подумать, что useEffect будет выполнен 3-й, но React достаточно умен, чтобы знать, когда состояние не изменилось, и, следовательно, не выполняет useEffect .

Добавьте зависимости к вашему useEffect , как указано выше. Это решит вашу проблему.

 useEffect(
  ...
, [trackIndex])