Использование асинхронного кода в Preact требует аккуратного подхода, особенно внутри функциональных компонентов и хуков. Несмотря на то, что Preact совместим с React-подходами, важно учитывать лёгкость и минимализм Preact, чтобы не создавать лишние перерендеры или утечки памяти.
useEffectХук useEffect является основным инструментом для работы
с побочными эффектами, включая асинхронные запросы. Прямое использование
async функции в useEffect невозможно, так как
сам эффект должен возвращать либо undefined, либо функцию
очистки:
import { h } from 'preact';
import { useEffect, useState } from 'preact/hooks';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let isMounted = true;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
if (isMounted) {
setData(result);
}
} catch (error) {
console.error(error);
} finally {
if (isMounted) {
setLoading(false);
}
}
}
fetchData();
return () => {
isMounted = false;
};
}, []);
return loading ? <p>Загрузка...</p> : <pre>{JSON.stringify(data, null, 2)}</pre>;
}
Ключевые моменты:
useEffect.isMounted предотвращает обновление состояния
после размонтирования компонента.try/catch обеспечивает стабильность
работы при сетевых сбоях.Создание собственных хуков для асинхронной логики повышает повторное использование кода. Важный аспект — корректное управление состоянием загрузки и результатами:
import { useState, useEffect } from 'preact/hooks';
function useAsync(fetchFunction) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let isMounted = true;
async function execute() {
setLoading(true);
setError(null);
try {
const result = await fetchFunction();
if (isMounted) setData(result);
} catch (err) {
if (isMounted) setError(err);
} finally {
if (isMounted) setLoading(false);
}
}
execute();
return () => {
isMounted = false;
};
}, [fetchFunction]);
return { data, loading, error };
}
Применение хука:
function App() {
const { data, loading, error } = useAsync(() => fetch('https://api.example.com/items').then(res => res.json()));
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error.message}</p>;
return <ul>{data.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}
Особенности:
isMounted предотвращает утечки памяти и ошибки
при обновлении состояния после размонтирования.Асинхронные функции можно использовать в обработчиках событий,
например, onClick. Важно правильно обрабатывать состояние и
ошибки:
function LoadButton() {
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
async function handleClick() {
setLoading(true);
try {
const response = await fetch('https://api.example.com/button-data');
const result = await response.json();
setData(result);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
}
return (
<div>
<button onCl ick={handleClick} disabled={loading}>
{loading ? 'Загрузка...' : 'Загрузить данные'}
</button>
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}
Замечания:
async функции в событиях не вызывают побочных эффектов
Preact, кроме как обычного обновления состояния.loading позволяет блокировать повторные
нажатия и улучшает UX.Асинхронные хуки и обработчики требуют надежного механизма обработки ошибок. Рекомендуется комбинировать:
try/catch для синхронной и асинхронной логики.error для отображения ошибок в
UI.isMounted)
для предотвращения утечек.useEffect(() => {
let isMounted = true;
async function load() {
try {
const result = await fetchData();
if (isMounted) setData(result);
} catch (err) {
if (isMounted) setError(err);
}
}
load();
return () => { isMounted = false; };
}, []);
Preact позволяет организовать несколько асинхронных вызовов внутри эффекта:
useEffect(() => {
let isMounted = true;
async function loadAll() {
try {
const [users, posts] = await Promise.all([
fetch('/users').then(res => res.json()),
fetch('/posts').then(res => res.json())
]);
if (isMounted) {
setUsers(users);
setPosts(posts);
}
} catch (error) {
console.error(error);
}
}
loadAll();
return () => { isMounted = false; };
}, []);
Promise.all позволяет выполнять запросы параллельно,
ускоряя загрузку данных.await
в цепочке вызовов.useCallback,
чтобы избежать лишних перезапусков useEffect.async в useEffect, всегда
оборачивая в внутреннюю функцию.Асинхронные операции в Preact требуют аккуратного управления
состоянием и размонтированием компонентов. Корректное применение
async/await в сочетании с хуками позволяет создавать
эффективные, стабильные и легко поддерживаемые приложения.