Привет, друзья! Я – Кирилл Мыльников, frontend-разработчик в ГК Юзтех. Сегодня мы поговорим о useEffect — одном из самых часто используемых хуков в React. Этот хук является мощным инструментом для управления побочными эффектами в функциональных компонентах. Мы рассмотрим, как он работает, на что следует обращать внимание и как избежать распространенных ошибок. Статья будет полезна как новичкам, так и опытным разработчикам.
Давайте начнем вот с такого примера и подумайте в каком порядке будут вызываться console.log?
Пример 1

Если вы ответили неправильно ничего страшного, как раз в этой статье разберем, что тут происходит. На самом деле это явно не документировано или объяснено на официальном сайте React.
Для начала вспомним что такое useEffect и как он работает:
useEffect — это асинхронный хук в React, который позволяет выполнять побочные эффекты в функциональных компонентах. Побочные эффекты могут включать в себя такие операции, как запросы данных, подписки, ручные изменения DOM и таймеры. useEffect заменяет методы жизненного цикла, такие как componentDidMount, componentDidUpdate и componentWillUnmount, которые используются в классовых компонентах
Особенности:
- Выполняется после рендеринга. useEffect выполняется после того, как компонент был отрендерен в DOM.Это гарантирует, что все элементы DOM доступны для манипуляции.
- Вы можете передавать вторым аргументом массив зависимостей, то данный хук будет выполнятся только тогда, когда изменятся данные значения.
- Mожет возвращать функцию очистки, которая будет вызвана перед тем, как компонент будет размонтирован или перед следующим выполнением useEffect. Это полезно для отмены подписок или таймеров.
Пример работы useEffect:

Также бонусом можно разобрать, как работает useLayoutEffect, принцип работы как у useEffect, только он синхронный и работает точно также как и в классовых компонентах
Пример работы useLayoutEffect:

Давайте вспомним, как работает жизненный цикл, который делится на 3 этапа: Trigger – Render – Commit
Trigger
Существует две причины для рендеринга компонента:
- Это начальный рендеринг компонента.
- Состояние компонента (или одного из его предков) обновлено.
Rendering
- React вызывает компонент и работает над обновленным состоянием
- Создает новый DOM узел.
Commit
- Манипуляция с DOM
- Запуск эффектов
React Fiber
React Fiber – это архитектура, используемая в React для управления иерархией компонентов и оптимизации процесса рендеринга. Она основа на деревовидной структуре, где каждый узел представляет собой компонент и его состояние.
Каждый узел в этом дереве называется «узлом волокна» (fiber node). Узлы волокна содержат информацию о props и state компонентов. При каждом обновлении состоянии React создается новое дерево волокон и сравнивает его со старым деревом, чтобы определить, какие изменения необходимо внести в DOM. Этот процесс позволяет React эффективно управлять обновлениями и обеспечивать плавный пользовательский интерфейс.
Отсюда следует, что React Fiber значительно улучшает производительность и отзывчивость приложений, особенно в случаях, когда требуется обработка большого количества обновлений
Пример React Fiber

Как правило, в React используется один и тот же алгоритм обхода во многих случаях
Render Phase
React Fiber работает, обходя дерево компонентов и выполняя два ключевых этапа на каждом узле.
На первом этапе React вызывает компонент и проводит процесс согласования (reconciliation). В этот момент React сравнивает текущее состояние и свойства компонента с новыми данными и помечает узлы волокон, которые требуют обновления.
На втором этапе React создает или обновляет соответствующие DOM-узлы на основе изменений, выявленных на первом этапе.
После завершения этих шагов React генерирует новое дерево волокон с обновленными DOM-узлами. Однако фактические изменения в DOM (мутации) происходят только на этапе коммита (commit phase), что обеспечивает плавное и эффективное обновление пользовательского интерфейса.
Commit Phase
Фаза фиксации, или коммита, — это этап, на котором происходят фактические изменения в DOM и выполняется очистка эффектов (useEffect). Хотя общий шаблон обхода дерева остается прежним, мутации DOM и очистка эффектов обрабатываются в отдельных проходах.
В этом разделе мы сосредоточимся исключительно на процессе очистки эффектов, оставив в стороне мутации DOM.
Сommitings Effects
React использует тот же алгоритм обхода дерева, но с одним важным отличием: вместо проверки наличия дочерних элементов у узла, он проверяет наличие поддерева. Это логично, так как только компоненты React могут содержать хуки useEffect. Например, узел DOM, такой как <p>, не будет содержать никаких хуков React.
На первом этапе обхода не происходит никаких действий. Однако на втором этапе выполняются определенные эффекты, связанные с хуками.
Этот подход объясняет, почему дочерние эффекты запускаются до родительских эффектов
Теперь, возвращаясь к первому примеру, должно стать понятным, почему так происходит
Пример:

Понимание того, как и когда React выполняет хуки useEffect, может значительно помочь в предотвращении мелких ошибок и неожиданного поведения, особенно при работе с комплексными структурами компонентов.
Материалы
react - https://react.dev/
useEffect – https://react.dev/reference/react/useEffect
useLayoutEffect – https://react.dev/reference/react/useLayoutEffect
Комментарии