Магия useEffect для начинающих
Понятие побочных эффектов
Компоненты как чистые функции
Представьте, что компонент React — это надежный повар, который готовит блюдо по строгому рецепту. Вы даете ему ингредиенты (данные, или «пропсы»), а он всегда возвращает вам одно и то же блюдо (часть пользовательского интерфейса). Если вы дадите ему те же самые ингредиенты завтра, он приготовит точно такое же блюдо.
В программировании такой предсказуемый процесс описывается понятием чистая функция (pure function). Главная задача чистой функции — вычислить и вернуть результат, не влияя ни на что за ее пределами. Компоненты React спроектированы так, чтобы в идеале вести себя именно как чистые функции. Их основная работа — это рендеринг: они принимают данные и на их основе описывают, как должен выглядеть интерфейс. Этот процесс должен быть предсказуемым.
Что такое побочные эффекты
Иногда компоненту нужно сделать что-то, что выходит за рамки простого отображения интерфейса. Например:
- Отправить данные на сервер: когда пользователь заполняет форму, ее нужно куда-то отправить.
- Изменить заголовок вкладки в браузере: чтобы он соответствовал открытой странице.
- Запустить таймер: например, для обратного отсчета.
- Подписаться на внешние события: например, отслеживать, в сети ли пользователь.
Все эти действия, которые происходят «за кулисами» и взаимодействуют с внешним миром (браузером, сервером, таймерами), называются побочными эффектами (side effects). Они необходимы для создания живых, интерактивных приложений, но они нарушают «чистоту» компонента, потому что их результат не связан напрямую с отрисовкой UI.
Побочный эффект — это любое взаимодействие с миром за пределами вашего компонента.
Почему эффекты нельзя вызывать напрямую
Может показаться логичным просто вставить код для побочного эффекта прямо в тело компонента. Например, чтобы изменить заголовок страницы:
function Greeting() {
// ❌ Неправильно! Эффект вызывается во время рендеринга.
document.title = "Добро пожаловать!";
return <h1>Привет, мир!</h1>;
}
Проблема в том, что React может вызывать функцию вашего компонента (то есть, проводить рендеринг) множество раз и по разным причинам, а не только при первом отображении. Например, когда состояние родительского компонента меняется, React перерисовывает и дочерние компоненты.
Если разместить побочный эффект прямо в теле функции, он будет выполняться при каждом рендеринге. Это может привести к неожиданному поведению и серьезным проблемам с производительностью. Например, если бы вы отправляли сетевой запрос при каждом рендеринге, вы могли бы легко перегрузить сервер или создать бесконечный цикл обновлений.
Решение: хук `useEffect`
Для безопасного управления побочными эффектами в React есть специальный инструмент — хук useEffect. Он позволяет «отложить» выполнение побочного эффекта до тех пор, пока React не закончит все расчеты и не обновит (то есть, не отрисует интерфейс на экране).
Думайте о useEffect как о специальном месте, куда вы выносите весь код, который не связан напрямую с отрисовкой. Это позволяет React сначала быстро показать пользователю обновленный интерфейс, а уже потом, в фоновом режиме, выполнить все необходимые побочные эффекты.
Вот как правильно изменить заголовок страницы:
import { useEffect } from 'react';
function Greeting() {
useEffect(() => {
// ✅ Правильно! Эффект выполняется после рендеринга.
document.title = "Добро пожаловать!";
});
return <h1>Привет, мир!</h1>;
}
Теперь изменение заголовка отделено от процесса рендеринга. React вызовет код внутри useEffect после того, как компонент будет отображен на странице, что делает наше приложение более предсказуемым и производительным.
Что из нижеперечисленного лучше всего описывает концепцию «чистой функции» в контексте компонентов React?
Почему размещение побочного эффекта (например, сетевого запроса) непосредственно в теле функции компонента является плохой практикой?