useEffect в библиотеке Atomico выполняет роль жизненного
цикла компонента, позволяя реагировать на изменения состояния или
пропсов. Часто возникает необходимость выполнять асинхронные
операции — запросы к API, задержки, работу с IndexedDB или
другими внешними источниками данных. Важно понимать, что функция,
передаваемая в useEffect, не может быть асинхронной
напрямую, то есть нельзя писать
async () => { ... }.
import { c, useState, useEffect } from "atomico";
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
async function fetchData() {
const response = await fetch("https://api.example.com/data");
const json = await response.json();
setData(json);
}
fetchData();
}, []);
return <host>{data ? JSON.stringify(data) : "Загрузка..."}</host>;
}
В этом примере создается внутренняя асинхронная
функция fetchData и вызывается внутри эффекта.
Такой подход позволяет корректно использовать await и
обрабатывать промисы.
При работе с асинхронными запросами важно учитывать, что компонент может размонтироваться до завершения операции. Для предотвращения ошибок используется флаг отмены:
useEffect(() => {
let cancelled = false;
async function fetchData() {
const response = await fetch("https://api.example.com/data");
if (!cancelled) {
const json = await response.json();
setData(json);
}
}
fetchData();
return () => {
cancelled = true;
};
}, []);
Флаг cancelled предотвращает вызов setData
после размонтирования компонента, что избавляет от предупреждений о
“setState on unmounted component”.
AbortControllerДля более строгого контроля над асинхронными запросами можно
применять AbortController. Это особенно полезно при работе
с fetch:
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
const response = await fetch("https://api.example.com/data", {
signal: controller.signal
});
const json = await response.json();
setData(json);
} catch (error) {
if (error.name !== "AbortError") {
console.error(error);
}
}
}
fetchData();
return () => controller.abort();
}, []);
Здесь при размонтировании компонента запрос автоматически
прерывается, а ошибки отмены (AbortError) игнорируются.
Асинхронные функции в useEffect тесно связаны с массивом
зависимостей. Любое изменение зависимостей приводит к повторному вызову
эффекта. При этом важно не создавать функцию внутри массива
зависимостей, иначе эффект будет срабатывать на каждое
изменение ссылки на функцию.
const [query, setQuery] = useState("atomico");
useEffect(() => {
let cancelled = false;
async function fetchData() {
const response = await fetch(`https://api.example.com/search?q=${query}`);
if (!cancelled) {
const json = await response.json();
setData(json);
}
}
fetchData();
return () => {
cancelled = true;
};
}, [query]); // эффект повторяется только при изменении query
Таким образом, асинхронные операции остаются контролируемыми и предсказуемыми, предотвращая лишние запросы и утечки памяти.
Для повторного использования логики асинхронных операций удобно создавать кастомные хуки:
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
async function load() {
setLoading(true);
try {
const response = await fetch(url, { signal: controller.signal });
const json = await response.json();
setData(json);
} catch (error) {
if (error.name !== "AbortError") console.error(error);
} finally {
setLoading(false);
}
}
load();
return () => controller.abort();
}, [url]);
return { data, loading };
}
Использование такого хука упрощает компонент:
function App() {
const { data, loading } = useFetch("https://api.example.com/data");
return <host>{loading ? "Загрузка..." : JSON.stringify(data)}</host>;
}
Кастомные хуки позволяют разделять логику асинхронных операций и представление, что делает код более читаемым и поддерживаемым.
async), необходимо создавать внутреннюю функцию.AbortController для прерывания
fetch-запросов.Эти практики позволяют безопасно интегрировать асинхронные операции в
useEffect без риска утечек памяти и непредсказуемого
поведения компонентов.