JS: React
Теория: Хук useEffect
Представим компонент, в котором нужно сделать запрос данных на сервер. Решение этой задачи в лоб может выглядеть как-то так:
В этом коде мы делаем запрос данных внутри функции компонента, а затем сохраняем полученные данные в стейт. Проблема этого кода в том, что запрос будет вызываться при каждом рендере.
Нужно запомнить одно простое правило: React сам решает, когда вызвать функцию рендера компонента. Поэтому код внутри функции Example может вызываться множество раз. В нашем примере произойдет бесконечный цикл:
- В обработчике запроса изменяется стейт
- Изменение стейта вызывает перерисовку компонента
- Перерисовка компонента вызывает
axios.get() - Снова происходит изменение стейта и так по кругу
Никогда не меняйте состояния в рендерах — с этим правилом мы уже знакомились, изучая концепцию MVC.
Выполнить подобные побочные эффекты помогает встроенный хук useEffect(). Именно его мы изучим в этом уроке.
Хук useEffect() заменяет три колбека жизненного цикла:
componentDidMount()componentDidUpdate()componentWillUnmount()
Подробнее об их работе можно прочитать в официальной документации.
Начнем с простого примера, в котором используется функция alert(). Вызов этой функции использует API браузера, поэтому он приносит с собой побочные эффекты:
Ниже пример, в котором меняется фон при каждом клике.
https://codepen.io/hexlet/pen/zYEGvXq
Колбек, переданный в useEffect(), отрабатывает после первой отрисовки и каждого обновления компонента. То есть произошло объединение методов componentDidUpdate() и componentDidMount(). Такое изменение было сделано ради удобства. Мировая практика использования React показала, что, в основном, эффекты происходят после каждого рендера, независимо от того, первая эта отрисовка или все последующие. Как бонус, сократилось количество дублирования и кода. Какие типичные сайд эффекты встречаются во фронтенде? Например:
- Извлечение данных
- Работа с BOM(Browser Object Model) API, например, Local Storage
- Прямое изменение DOM, сюда же относятся библиотеки не совместимые с React
Действие хука useEffect() иногда можно пропускать. Такое бывает полезно либо в целях оптимизации, либо, если эффект имеет смысл только при определенных условиях. Для этого в хук вторым аргументом передается массив значений, которые надо отслеживать между отрисовками. Если хотя бы одно значение из этого массива поменялось, то колбек вызывается, если все значения остались прежними — пропускается.
То есть колбек будет вызван только тогда, когда изменится count. Таким же способом можно передать любой набор переменных, который мы бы хотели связать с изменением эффекта. Если хотя бы одна переменная в переданном массиве поменялась, то эффект сработает, иначе React его пропускает.
Что делать, если нужно запустить useEffect() только на момент первого рендера (сразу после монтирования)? Для этого достаточно передать пустой массив:
Решение не самое очевидное, но технически оно не является особым случаем. К нему нужно просто привыкнуть.
Сброс эффекта
В некоторых случаях эффект нужно сбрасывать. Например, когда эффект после изменения пропсов перестает быть актуальным, его нужно «зачистить». Для этого достаточно вернуть функцию из useEffect(), внутри которой выполняется очистка:
Изменение userId приведет к сбросу текущего таймера и установке нового. Подобный код в классах потребовал бы использования аж 4 колбеков жизненного цикла.
Для имитации componentWillUnmount() достаточно соединить очистку с пустым массивом вторым параметром:
Асинхронные запросы
Первым параметром useEffect() принимает функцию. Эта функция должна либо ничего не возвращать, либо возвращать функцию для сброса эффекта. Это накладывает некоторое ограничение на использование async await:
Если использовать async, то функция уже возвращает промис — это нарушает правило выше. Чтобы этого избежать, можно обернуть асинхронный вызов в функцию и вызвать эту функцию внутри useEffect:




