1 added
1 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Представим компонент, в котором нужно сделать запрос данных на сервер. Решение этой задачи в лоб может выглядеть как-то так:</p>
1
<p>Представим компонент, в котором нужно сделать запрос данных на сервер. Решение этой задачи в лоб может выглядеть как-то так:</p>
2
<p>В этом коде мы делаем запрос данных внутри функции компонента, а затем сохраняем полученные данные в стейт. Проблема этого кода в том, что запрос будет вызываться при каждом рендере.</p>
2
<p>В этом коде мы делаем запрос данных внутри функции компонента, а затем сохраняем полученные данные в стейт. Проблема этого кода в том, что запрос будет вызываться при каждом рендере.</p>
3
<p>Нужно запомнить одно простое правило: React сам решает, когда вызвать функцию рендера компонента. Поэтому код внутри функции Example может вызываться множество раз. В нашем примере произойдет бесконечный цикл:</p>
3
<p>Нужно запомнить одно простое правило: React сам решает, когда вызвать функцию рендера компонента. Поэтому код внутри функции Example может вызываться множество раз. В нашем примере произойдет бесконечный цикл:</p>
4
<ul><li>В обработчике запроса изменяется стейт</li>
4
<ul><li>В обработчике запроса изменяется стейт</li>
5
<li>Изменение стейта вызывает перерисовку компонента</li>
5
<li>Изменение стейта вызывает перерисовку компонента</li>
6
<li>Перерисовка компонента вызывает axios.get()</li>
6
<li>Перерисовка компонента вызывает axios.get()</li>
7
<li>Снова происходит изменение стейта и так по кругу</li>
7
<li>Снова происходит изменение стейта и так по кругу</li>
8
</ul><p>Никогда не меняйте состояния в рендерах - с этим правилом мы уже знакомились, изучая концепцию MVC.</p>
8
</ul><p>Никогда не меняйте состояния в рендерах - с этим правилом мы уже знакомились, изучая концепцию MVC.</p>
9
<p>Выполнить подобные побочные эффекты помогает встроенный хук useEffect(). Именно его мы изучим в этом уроке.</p>
9
<p>Выполнить подобные побочные эффекты помогает встроенный хук useEffect(). Именно его мы изучим в этом уроке.</p>
10
<p>Хук useEffect() заменяет три колбека жизненного цикла:</p>
10
<p>Хук useEffect() заменяет три колбека жизненного цикла:</p>
11
<ul><li>componentDidMount()</li>
11
<ul><li>componentDidMount()</li>
12
<li>componentDidUpdate()</li>
12
<li>componentDidUpdate()</li>
13
<li>componentWillUnmount()</li>
13
<li>componentWillUnmount()</li>
14
</ul><p>Подробнее об их работе можно прочитать в<a>официальной документации</a>.</p>
14
</ul><p>Подробнее об их работе можно прочитать в<a>официальной документации</a>.</p>
15
<p>Начнем с простого примера, в котором используется функция alert(). Вызов этой функции использует API браузера, поэтому он приносит с собой побочные эффекты:</p>
15
<p>Начнем с простого примера, в котором используется функция alert(). Вызов этой функции использует API браузера, поэтому он приносит с собой побочные эффекты:</p>
16
<p>Ниже пример, в котором меняется фон при каждом клике.</p>
16
<p>Ниже пример, в котором меняется фон при каждом клике.</p>
17
-
<p><a>https://codepen.io/hexlet/pen/zYEGvXq</a></p>
17
+
<p><a>Попрактиковаться</a></p>
18
<p>Колбек, переданный в useEffect(), отрабатывает после первой отрисовки и каждого обновления компонента. То есть произошло объединение методов componentDidUpdate() и componentDidMount(). Такое изменение было сделано ради удобства. Мировая практика использования React показала, что, в основном, эффекты происходят после каждого рендера, независимо от того, первая эта отрисовка или все последующие. Как бонус, сократилось количество дублирования и кода. Какие типичные сайд эффекты встречаются во фронтенде? Например:</p>
18
<p>Колбек, переданный в useEffect(), отрабатывает после первой отрисовки и каждого обновления компонента. То есть произошло объединение методов componentDidUpdate() и componentDidMount(). Такое изменение было сделано ради удобства. Мировая практика использования React показала, что, в основном, эффекты происходят после каждого рендера, независимо от того, первая эта отрисовка или все последующие. Как бонус, сократилось количество дублирования и кода. Какие типичные сайд эффекты встречаются во фронтенде? Например:</p>
19
<ul><li>Извлечение данных</li>
19
<ul><li>Извлечение данных</li>
20
<li>Работа с BOM(Browser Object Model) API, например, Local Storage</li>
20
<li>Работа с BOM(Browser Object Model) API, например, Local Storage</li>
21
<li>Прямое изменение DOM, сюда же относятся библиотеки не совместимые с React</li>
21
<li>Прямое изменение DOM, сюда же относятся библиотеки не совместимые с React</li>
22
</ul><p>Действие хука useEffect() иногда можно пропускать. Такое бывает полезно либо в целях оптимизации, либо, если эффект имеет смысл только при определенных условиях. Для этого в хук вторым аргументом передается массив значений, которые надо отслеживать между отрисовками. Если хотя бы одно значение из этого массива поменялось, то колбек вызывается, если все значения остались прежними - пропускается.</p>
22
</ul><p>Действие хука useEffect() иногда можно пропускать. Такое бывает полезно либо в целях оптимизации, либо, если эффект имеет смысл только при определенных условиях. Для этого в хук вторым аргументом передается массив значений, которые надо отслеживать между отрисовками. Если хотя бы одно значение из этого массива поменялось, то колбек вызывается, если все значения остались прежними - пропускается.</p>
23
<p>То есть колбек будет вызван только тогда, когда изменится count. Таким же способом можно передать любой набор переменных, который мы бы хотели связать с изменением эффекта. Если хотя бы одна переменная в переданном массиве поменялась, то эффект сработает, иначе React его пропускает.</p>
23
<p>То есть колбек будет вызван только тогда, когда изменится count. Таким же способом можно передать любой набор переменных, который мы бы хотели связать с изменением эффекта. Если хотя бы одна переменная в переданном массиве поменялась, то эффект сработает, иначе React его пропускает.</p>
24
<p>Что делать, если нужно запустить useEffect() только на момент первого рендера (сразу после монтирования)? Для этого достаточно передать пустой массив:</p>
24
<p>Что делать, если нужно запустить useEffect() только на момент первого рендера (сразу после монтирования)? Для этого достаточно передать пустой массив:</p>
25
<p>Решение не самое очевидное, но технически оно не является особым случаем. К нему нужно просто привыкнуть.</p>
25
<p>Решение не самое очевидное, но технически оно не является особым случаем. К нему нужно просто привыкнуть.</p>
26
<h2>Сброс эффекта</h2>
26
<h2>Сброс эффекта</h2>
27
<p>В некоторых случаях эффект нужно сбрасывать. Например, когда эффект после изменения пропсов перестает быть актуальным, его нужно "зачистить". Для этого достаточно вернуть функцию из useEffect(), внутри которой выполняется очистка:</p>
27
<p>В некоторых случаях эффект нужно сбрасывать. Например, когда эффект после изменения пропсов перестает быть актуальным, его нужно "зачистить". Для этого достаточно вернуть функцию из useEffect(), внутри которой выполняется очистка:</p>
28
<p>Изменение userId приведет к сбросу текущего таймера и установке нового. Подобный код в классах потребовал бы использования аж 4 колбеков жизненного цикла.</p>
28
<p>Изменение userId приведет к сбросу текущего таймера и установке нового. Подобный код в классах потребовал бы использования аж 4 колбеков жизненного цикла.</p>
29
<p>Для имитации componentWillUnmount() достаточно соединить очистку с пустым массивом вторым параметром:</p>
29
<p>Для имитации componentWillUnmount() достаточно соединить очистку с пустым массивом вторым параметром:</p>
30
<h2>Асинхронные запросы</h2>
30
<h2>Асинхронные запросы</h2>
31
<p>Первым параметром useEffect() принимает функцию. Эта функция должна либо ничего не возвращать, либо возвращать функцию для сброса эффекта. Это накладывает некоторое ограничение на использование async await:</p>
31
<p>Первым параметром useEffect() принимает функцию. Эта функция должна либо ничего не возвращать, либо возвращать функцию для сброса эффекта. Это накладывает некоторое ограничение на использование async await:</p>
32
<p>Если использовать async, то функция уже возвращает промис - это нарушает правило выше. Чтобы этого избежать, можно обернуть асинхронный вызов в функцию и вызвать эту функцию внутри useEffect:</p>
32
<p>Если использовать async, то функция уже возвращает промис - это нарушает правило выше. Чтобы этого избежать, можно обернуть асинхронный вызов в функцию и вызвать эту функцию внутри useEffect:</p>
33
33