Preact предоставляет легковесную альтернативу React с почти идентичным API, включая поддержку функциональных и классовых компонентов. Одной из ключевых задач при разработке интерфейсов является асинхронная обработка данных, чаще всего с использованием промисов. Понимание того, как корректно интегрировать промисы в компоненты, является основой для построения отзывчивых и эффективных приложений.
Функциональные компоненты в Preact используют хуки
для управления состоянием и побочными эффектами. Основным инструментом
для работы с промисами является хук useEffect совместно с
useState.
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
function DataFetcher({ url }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(url)
.then(response => {
if (!response.ok) throw new Error('Ошибка загрузки данных');
return response.json();
})
.then(json => setData(json))
.catch(err => setError(err))
.finally(() => setLoading(false));
}, [url]);
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error.message}</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
Ключевые моменты:
useEffect используется для запуска асинхронного кода
после рендера компонента.[url] гарантируют повторный вызов эффекта
при изменении адреса запроса.finally позволяет завершить загрузку независимо от
результата промиса..catch предотвращает падение
компонента.useEffectВызов асинхронных функций напрямую в useEffect
невозможен, так как эффект не может возвращать промис. Вместо этого
создается внутренняя асинхронная функция:
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
if (!response.ok) throw new Error('Ошибка загрузки данных');
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
Преимущества такого подхода:
async/await.try/catch.loading, data и
error централизовано.При быстром изменении пропсов или размонтировании компонента важно предотвращать изменение состояния после того, как компонент уже не существует. Для этого используется флаг отмены:
useEffect(() => {
let isCancelled = false;
const fetchData = async () => {
try {
const response = await fetch(url);
const result = await response.json();
if (!isCancelled) setData(result);
} catch (err) {
if (!isCancelled) setError(err);
}
};
fetchData();
return () => {
isCancelled = true;
};
}, [url]);
Флаг isCancelled предотвращает вызов
setState после размонтирования, что исключает появление
предупреждений типа “Can’t perform a React state update on an unmounted
component”.
Классовые компоненты в Preact также поддерживают работу с промисами
через методы жизненного цикла. Наиболее часто используется
componentDidMount:
import { h, Component } from 'preact';
class DataFetcherClass extends Component {
state = {
data: null,
loading: true,
error: null
};
componentDidMount() {
fetch(this.props.url)
.then(response => {
if (!response.ok) throw new Error('Ошибка загрузки');
return response.json();
})
.then(data => this.setState({ data }))
.catch(error => this.setState({ error }))
.finally(() => this.setState({ loading: false }));
}
render() {
const { data, loading, error } = this.state;
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error.message}</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
}
Особенности:
this.setState.AbortController.AbortController для отмены запросовAbortController позволяет явно прерывать асинхронные
запросы, предотвращая лишние обновления состояния:
useEffect(() => {
const controller = new AbortController();
const fetchData = async () => {
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error('Ошибка загрузки');
const result = await response.json();
setData(result);
} catch (err) {
if (err.name !== 'AbortError') setError(err);
} finally {
setLoading(false);
}
};
fetchData();
return () => controller.abort();
}, [url]);
Преимущества использования AbortController:
Для работы с несколькими асинхронными операциями одновременно можно
использовать Promise.all или
Promise.allSettled:
useEffect(() => {
const fetchMultiple = async () => {
try {
setLoading(true);
const [usersResponse, postsResponse] = await Promise.all([
fetch('/api/users'),
fetch('/api/posts')
]);
const users = await usersResponse.json();
const posts = await postsResponse.json();
setData({ users, posts });
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchMultiple();
}, []);
Promise.all завершится с ошибкой, если хотя бы один
промис не выполнен.Promise.allSettled позволяет обработать все результаты
независимо от ошибок.Правильная обработка промисов в Preact требует понимания особенностей
хуков и жизненного цикла компонентов. Использование
useEffect с асинхронными функциями, флагов отмены или
AbortController позволяет создавать надежные компоненты,
которые корректно обрабатывают асинхронные операции и избегают проблем с
состоянием. Комбинация этих методов обеспечивает как чистоту кода, так и
устойчивость интерфейса к быстрым изменениям данных.