Я проводил некоторые тесты со следующим фиктивным компонентом, где после выполнения (в консоли) я получаю следующий результат:

Rendering:  0
Triggered:  0
Rendering:  4
Triggered:  4
Rendering:  4

Я изо всех сил пытаюсь понять, почему.

Первый рендеринг:

  • устанавливает index в 0.
  • запускает useEffect, поскольку undefined не является 0
  • useEffect запрашивает настройку index на 4

Второй рендер:

  • index равно 4
  • почему тело useEffect выполняется снова?

Третий рендер:

  • почему есть повторный рендеринг?

То, что я ожидал бы это:

Rendering:  0
Triggered:  0
Rendering:  4

Я пропускаю что-то очень очевидное? Не могли бы вы помочь мне понять, как это работает под капотом?

const Example = React.memo(() => {
    const [index, setIndex] = React.useState(0)
    console.log('Rendering: ', index)

    React.useEffect(() => {
        console.log('Triggered: ', index)
        setIndex(4)
    }, [index])

    return <h1>{index}</h1>
})

ReactDOM.render(<Example />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
2
Greg Wozniak 19 Янв 2022 в 13:39
1
"запускает useEffect as undefined is not 0" — useEffect не выполняется из-за начального значения index; по умолчанию useEffect выполняется после начального рендеринга. "почему тело useEffect выполняется снова?" — потому что useEffect зависит от index, и вы изменили состояние внутри useEffect, установив index на 4.
 – 
Yousaf
19 Янв 2022 в 13:52

3 ответа

Количество повторных рендеров на самом деле верное. Давайте проанализируем, что происходит:


  1. Визуализация 0 — это первая визуализация при монтировании (в этом состоянии каждый useEffect запускается автоматически)

  1. Инициировано 0 — это начальное срабатывание useEffect.

  1. Визуализация 4 – эта повторная визуализация вызвана тем, что вы установили для нового состояния index значение 4

  1. Триггер 4 – это вызвано тем, что вы изменили index с 0 на 4.

  1. Визуализация 4 – это выполняется, поскольку состояние изменяется с 4 на 4 (даже если значение остается тем же, оно вызовет повторную визуализацию компонента, поскольку React не знает, установили ли вы то же состояние, и его нужно запустить снова). В этом повторном рендеринге useEffect не запускается, поскольку index снова равно 4.

Обоснование

Если что-либо изменится в компоненте в какой-либо момент, например useState useContext, customHook, этот компонент необходимо будет перерисовать. Это также происходит, если родительский компонент перерисовывается, дочерний компонент будет перерисовываться, даже если в вашем компоненте не было никаких изменений.

Дополнительные советы и информация по ререндерингу

Чтобы предотвратить ненужный повторный рендеринг родительских компонентов дочерних компонентов, вы можете использовать React.memo. , это приведет к повторному рендерингу вашего компонента, если реквизиты изменятся, но не предотвратит повторный рендеринг вашего компонента, если там изменится состояние или какой-либо из вышеперечисленных хуков.

Если это кажется излишним, то звучит так, но это необходимо, чтобы реакция была уверена, что имеет самое последнее состояние. Такой запуск JS быстрее, чем наличие некоторой памяти состояния, которая проверяет изменения, а также более прозрачен для разработчика (и как VDOM создается)

3
Mario Petrovic 19 Янв 2022 в 15:08
Спасибо. Я изменил компонент, добавив оболочку React.memo(), однако это не помешало последнему рендерингу. Не могли бы вы помочь мне понять, почему?
 – 
Greg Wozniak
19 Янв 2022 в 14:56
React memo не предотвратит этот последний повторный рендеринг, поскольку этот повторный рендеринг является законным, Reac.memo предназначен для предотвращения повторного рендеринга дочернего компонента при повторном рендеринге родительских компонентов.
 – 
Mario Petrovic
19 Янв 2022 в 15:07

Ожидаемый результат не может быть достигнут.

UseEffect отображается при монтировании компонента и при каждом повторном рендеринге при изменении состояния (индекс).

const Example = () => {
    const [index, setIndex] = React.useState(0)
    console.log('Rendering: ', index)
    React.useEffect(() => {
        console.log('Triggered: ', index)
        setIndex(4)
    }, [index])

    return <h1>{index}</h1>
}

Выход:

  • Визуализация: 0 => Когда компонент монтируется в первый раз, этот временной индекс равен 0
  • Triggered: 0 => При монтировании срабатывает useEffect и состояние обновляется (от 0 до 4)
  • Визуализация: 4 => Компонент повторно визуализируется, так как состояние изменилось с (0 на 4)
  • Triggered: 4 => useEffect срабатывает повторно, так как состояние изменилось (от 0 до 4)
  • Визуализация: 4 => Компонент повторно визуализировался, чтобы проверить, изменилось ли состояние или нет (от 4 до 4, вид подтверждения, что ничего не изменилось)
2
Rahul Sharma 19 Янв 2022 в 13:54

Способ работы useEffect заключается в том, что он запускается только после рендеринга компонента страницы.

Надеюсь, это поможет вам понять.

1-й индекс рендеринга равен 0.

Rendering:  0

Он запускает useEffect.

Triggered:  0

Индекс обновляется в useEffect, но не в компоненте. Затем рендер обновляет индекс до 4.

Rendering:  4

UseEffect запускается, потому что индекс изменился.

Triggered:  4

Компонент визуализируется снова, но индекс по-прежнему равен 4, поэтому useEffect больше не запускается.

Rendering:  4
1
user3767285 19 Янв 2022 в 13:55