Подробное руководство по хуку useEffect в React
14 февраля 2023 г.
Хук React useEffect
— один из самых сложных и мощных хуков в функциональных компонентах, поскольку он позволяет вам выполнять побочные эффекты в реакции. Но что такое побочный эффект? В React «побочный эффект» — это любая операция, которая влияет на что-то за пределами компонента, например вызов REST API, обновление DOM и т. д. Она будет выполняться после рендеринга компонента. Если вы знакомы с компонентами на основе классов, то ловушку useEffect можно легко понять как комбинацию методов жизненного цикла componentDidMount, componentDidUpdate и componentWillUnmount.
В этой статье мы подробно рассмотрим использование хука useEffect с примерами.
Когда использовать хук эффекта
Хук useEffect следует использовать каждый раз, когда вам нужен функциональный компонент для выполнения побочного эффекта. Это может включать такие вещи, как извлечение данных, настройка подписок и обновление ДОМ. Важно отметить, что хук useEffect не следует использовать для целей рендеринга, поскольку он не предназначен для замены механизма рендеринга React.
Некоторые сценарии, в которых может пригодиться хук useEffect, включают:
* Извлечение данных из API и обновление состояния компонента на основе ответа API. * Настройка подписки на источник данных и обновление состояния компонента при получении новых данных. * Извлечение/сохранение данных из localStorage * Добавление и удаление прослушивателей событий.
синтаксис использования
Синтаксис хука useEffect следующий:
useEffect(() => {
// function body
}, [dependencies]);
Хук useEffect
вызывается внутри функционального компонента и принимает два аргумента: функцию, представляющую тело эффекта, и необязательный массив зависимостей. Функция эффекта выполняется после рендеринга компонента. Когда указан массив зависимостей и изменяются значения аргументов в массиве зависимостей, будет запущен повторный запуск эффекта.
Ниже приведен синтаксис хука useEffect с функцией очистки.
useEffect(() => {
// effect function
return () => {
// cleanup function
};
}, [dependencies]);
Функция эффекта может возвращать функцию cleanup
, которая будет запущена перед повторным запуском эффекта или перед размонтированием компонента. Эту функцию очистки можно использовать для выполнения любых необходимых операций очистки, таких как отмена сетевых запросов, удаление прослушивателей событий, отказ от подписки на источники данных и т. д.
В одном и том же функциональном компоненте может быть несколько useEffect.
Как использовать хук эффекта
Чтобы использовать хук useEffect, сначала необходимо импортировать его из библиотеки react
. Затем вы можете вызвать функцию useEffect в своем компоненте и передать функцию, которая представляет эффект, который вы хотите выполнить.
import { useEffect } from "react";
function MyComponent() {
useEffect(() => {
// Your effect function here
}, []);
return <div>Hello World</div>;
}
Давайте рассмотрим подробное использование useEffect с примерами,
Пример 1: без передачи массива зависимостей
Если массив зависимостей вообще не указан, то useEffect будет выполняться каждый раз при рендеринге компонента.
import { useEffect } from "react";
function MyComponent() {
useEffect(() => {
console.log("This will be run every time the component renders");
});
return <div>Hello World</div>;
}
Это нетипичный случай, и обычно мы не используем этот сценарий в приложениях реального времени.
Пример 2. Передача пустого массива зависимостей
При передаче пустого массива зависимостей хук useEffect будет выполняться только один раз, когда компонент монтируется в DOM. Допустим, нам нужно получить сообщения в блоге автора после того, как он войдет в систему. В этом сценарии достаточно получить сообщения в блоге только один раз, а не каждый раз при повторном отображении компонента.
import { useEffect, useState } from "react";
function Posts() {
const [posts, setposts] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users/1/posts")
.then((resp) => resp.json())
.then((blogPosts) => setposts(blogPosts));
}, []);
return (
<div className="App">
{posts && posts.map((post) => <li>{post.title}</li>)}
</div>
);
}
export default Posts;
В приведенном выше примере мы извлекаем сообщения пользователя только один раз и отображаем их в DOM только один раз.
Некоторые другие сценарии, в которых вы будете передавать пустой массив зависимостей.
* Если вы хотите обновить заголовок страницы при посещении определенной страницы. * Когда вы хотите отправлять аналитические данные на ваш сервер, когда пользователь посещает определенную страницу. (Например, счетчик посещений страницы)
Пример 3. Передача аргументов в массив зависимостей
Когда аргумент передается в массив зависимостей, это гарантирует, что эффект будет повторно запускаться при каждом изменении его значения.
Допустим, нам нужно реализовать функцию поиска, которая фильтрует статьи/сообщения в блогах на основе ключевого слова, введенного пользователем. В этом случае мы можем передать ключевое слово поиска в качестве аргумента и реализовать логику фильтра в теле эффекта.
import { useEffect, useState } from "react";
function Search() {
const [posts, setposts] = useState([]);
const [search, setsearch] = useState("");
useEffect(() => {
const filteredPosts = posts.filter((p) => p.title.includes(search));
setposts(filteredPosts);
}, [search]);
return (
<div className="App">
{posts && (
<input
type="text"
value={search}
onChange={(e) => setsearch(e.target.value)}
/>
)}
{posts && posts.map((post) => <li>{post.title}</li>)}
</div>
);
}
export default Search;
Таким образом, всякий раз, когда пользователь вводит поисковый запрос, состояние search
изменяется и приводит к повторному запуску эффекта.
Пример 4. С функцией очистки
Во всех приведенных выше примерах мы не использовали дополнительную функцию очистки. Но будет несколько случаев, когда нам может понадобиться использовать функцию очистки.
Допустим, нам нужно реализовать сценарий, в котором, когда пользователь нажимает кнопку, отображается раскрывающийся список. И когда пользователь щелкает в любом месте за пределами раскрывающегося списка, он должен автоматически закрыть раскрывающийся список. Для этого мы можем использовать прослушиватели событий.
import { useEffect, useRef, useState } from "react";
function Dropdown() {
const ref = useRef(null);
const [open, setOpen] = useState(false);
const [options, setoptions] = useState([
{ key: 1, value: "Audi" },
{ key: 2, value: "BMW" },
{ key: 3, value: "Jaguar" },
{ key: 4, value: "Ferrari" }
]);
const [option, setOption] = useState("");
useEffect(() => {
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setOpen(false);
}
};
document.addEventListener("click", handleClickOutside);
return () => document.removeEventListener("click", handleClickOutside);
}, []);
return (
<div ref={ref}>
<button onClick={() => setOpen(!open)}>Toggle Dropdown</button>
{open && (
<ul>
{options.map((option) => (
<li key={option.key} onClick={() => setOption(option.value)}>
{option.value}
</li>
))}
</ul>
)}
</div>
);
}
export default Dropdown;
В этом примере мы настроили прослушиватель событий DOM, который закрывает раскрывающийся список, когда пользователь щелкает за пределами элемента раскрывающегося списка. Пустой массив зависимостей гарантирует, что эффект запускается только один раз, при монтировании, а функция очистки используется для удаления прослушивателя событий при размонтировании компонента.
Некоторые другие сценарии, когда вы хотите реализовать функцию очистки.
* В приложении чата на основе сокета, когда пользователь покидает комнату чата, нам нужно реализовать функцию очистки, чтобы отключиться от сокета. * Если вы используете хук useEffect для настройки подписки на события или данные, вы должны включить функцию очистки, которая отменяет подписку на эти события или данные, когда компонент отключается или эффект перезапускается.
Как не стоит использовать эффектный хук (с примерами)
В предыдущем разделе мы видели различные примеры использования хука useEffect. В этом разделе мы увидим "Как не надо", т.е. распространенные ошибки, которые допускают разработчики при использовании хука useEffect.
Пример 1:
import { useEffect, useState } from "react";
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1);
});
return <div>{count}</div>;
}
В этом примере хук useEffect вызывается без массива зависимостей, в результате чего функция эффекта выполняется при каждом рендеринге. Это приводит к бесконечному циклу, поскольку функция эффекта обновляет состояние счетчика, вызывая повторную визуализацию компонента и повторный запуск эффекта.
Пример 2. Пустой массив зависимостей не передается
Если вы не включите пустой массив зависимостей, когда это необходимо, useEffect будет повторно запускаться при каждом рендеринге, что может привести к проблемам с производительностью вашего приложения.
Например, рассмотрим тот же пример, который мы использовали в примере 2 предыдущего раздела, но без передачи массива зависимостей
import { useEffect, useState } from "react";
function Posts() {
const [posts, setposts] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users/1/posts")
.then((resp) => resp.json())
.then((blogPosts) => setposts(blogPosts));
});
return (
<div className="App">
{posts && posts.map((post) => <li>{post.title}</li>)}
</div>
);
}
export default Posts;
Таким образом, в этом случае при каждом рендеринге компонента будет выполняться вызов API для извлечения данных из внутреннего API, который не нужен и потребляет дополнительный сетевой трафик, тем самым влияя на производительность приложения.
Пример 3. Добавление ненужных зависимостей
Если вы включите ненужные зависимости в массив зависимостей хука useEffect, эффект будет запущен повторно без необходимости и потенциально может вызвать проблемы с производительностью в вашем приложении.
import { useEffect } from "react";
function TodoList({ todos, filter }) {
useEffect(() => {
console.log("filtering todos");
// filter todos
}, [todos, filter]);
return <div>{/* todo list JSX */}</div>;
}
В приведенном выше примере хук useEffect настроен на фильтрацию массива задач при изменении реквизитов задач или фильтра. Однако реквизит фильтра не используется в эффекте и поэтому не должен включаться в массив зависимостей. Это может привести к ненужному повторному запуску эффекта при изменении реквизита фильтра.
Пример 4. Без учета функций очистки
Если вы не включите функцию очистки в обработчик useEffect, но настроите любые ресурсы, которые необходимо очистить (например, прослушиватели событий DOM, интервалы, соединения сокетов и т. д.), это приведет к утечке памяти. и проблемы с производительностью.
Например, рассмотрим тот же сценарий, который мы использовали в примере 4 предыдущего раздела, но без использования функции очистки.
import { useEffect, useRef, useState } from "react";
function Dropdown() {
const ref = useRef(null);
const [open, setOpen] = useState(false);
const [options, setoptions] = useState([
{ key: 1, value: "Audi" },
{ key: 2, value: "BMW" },
{ key: 3, value: "Jaguar" },
{ key: 4, value: "Ferrari" }
]);
const [option, setOption] = useState("");
useEffect(() => {
const handleClickOutside = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setOpen(false);
}
};
document.addEventListener("click", handleClickOutside);
// No Cleanup function
}, []);
return (
<div ref={ref}>
<button onClick={() => setOpen(!open)}>Toggle Dropdown</button>
{open && (
<ul>
{options.map((option) => (
<li key={option.key} onClick={() => setOption(option.value)}>
{option.value}
</li>
))}
</ul>
)}
</div>
);
}
export default Dropdown;
Если мы не включим функцию очистки, то прослушиватель событий DOM, который мы создали в теле эффекта, не будет удален при размонтировании компонента.
Если прослушиватель событий не удаляется при размонтировании компонента, он будет продолжать прослушивать щелчки по документу, даже если компонент больше не отображается. Это может привести к утечке памяти, поскольку прослушиватель событий будет продолжать потреблять ресурсы, даже если он больше не нужен. Таким образом, всегда необходимо включать функцию очистки в useEffect, которая удаляет любые прослушиватели событий DOM при размонтировании компонента. Это обеспечит правильную очистку прослушивателей событий и освобождение ресурсов, когда они больше не нужны.
Заключение
В этой статье мы рассмотрели использование useEffect и рассмотрели примеры того, как и как не использовать хук Effect. В заключение отметим, что хук useEffect — это мощный инструмент в React, позволяющий выполнять побочные эффекты в функциональных компонентах. Важно правильно использовать хук useEffect, чтобы избежать проблем с производительностью. Следуя рекомендациям и избегая распространенных ошибок, как описано в статье, вы сможете эффективно управлять побочными эффектами в своих проектах React.
Также опубликовано здесь
Оригинал