Работа с сетевыми запросами является критически важной частью современных веб-приложений. Preact, как легковесный фреймворк для построения интерфейсов, не накладывает ограничений на способы обработки сетевых ошибок, но предоставляет удобные возможности интеграции с асинхронными операциями и управлением состоянием.
Основной инструмент для выполнения сетевых запросов в Preact — это
стандартный API fetch. Взаимодействие с ним требует
внимательного подхода к обработке ошибок, так как ошибки могут
возникнуть на разных уровнях:
Пример безопасного запроса с обработкой ошибок:
import { useState, useEffect } from 'preact/hooks';
function DataFetcher({ url }) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
setLoading(true);
setError(null);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Ошибка HTTP: ${response.status}`);
}
const json = await response.json();
setData(json);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchData();
}, [url]);
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Произошла ошибка: {error}</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
В этом примере ключевым моментом является разделение состояния загрузки, данных и ошибки, что обеспечивает предсказуемое поведение компонента.
Ошибки асинхронных операций необходимо оборачивать в блоки
try/catch, чтобы предотвратить падение всего приложения. В
Preact это особенно важно при работе с хуками useEffect,
так как необработанные исключения могут привести к некорректному
обновлению DOM.
useEffect(() => {
const controller = new AbortController();
async function load() {
try {
const res = await fetch('/api/data', { signal: controller.signal });
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
const result = await res.json();
setData(result);
} catch (err) {
if (err.name === 'AbortError') return;
setError(err.message);
}
}
load();
return () => controller.abort();
}, []);
Использование AbortController позволяет безопасно
прерывать запросы при размонтировании компонента и предотвращает ошибки
при попытке обновления состояния уже после удаления компонента из
DOM.
Для крупных приложений удобным подходом является выделение отдельного состояния для ошибок и их типов:
const [state, setState] = useState({
data: null,
loading: false,
networkError: null,
validationError: null
});
async function fetchData() {
setState(prev => ({ ...prev, loading: true, networkError: null }));
try {
const response = await fetch('/api/items');
if (!response.ok) throw new Error('Network response was not ok');
const data = await response.json();
setState(prev => ({ ...prev, data }));
} catch (err) {
setState(prev => ({ ...prev, networkError: err.message }));
} finally {
setState(prev => ({ ...prev, loading: false }));
}
}
Разделение ошибок по категориям позволяет гибко реагировать на разные сценарии: повторный запрос при сетевых сбоях или отображение сообщений валидации.
Для обработки ошибок на уровне всего приложения применяются методы
типа Error Boundaries, доступные через компонент
ErrorBoundary. Хотя Preact не поддерживает их в точности
как React, существуют легковесные реализации:
import { Component } from 'preact';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
componentDidCatch(error) {
this.setState({ hasError: true, error });
console.error(error);
}
render() {
if (this.state.hasError) {
return <p>Произошла критическая ошибка: {this.state.error.message}</p>;
}
return this.props.children;
}
}
Использование такого компонента позволяет перехватывать непредвиденные ошибки во всех дочерних компонентах, в том числе возникающие при сетевых запросах, и предотвращать падение приложения.
Для повышения устойчивости к нестабильным сетям применяются повторные попытки запросов и таймауты:
async function fetchWithRetry(url, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) throw new Error(`HTTP error ${response.status}`);
return await response.json();
} catch (err) {
if (i === retries - 1) throw err;
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
Этот подход позволяет автоматически повторять запросы при временных сбоях, избегая необходимости ручного вмешательства.
Обработка сетевых ошибок в Preact часто сочетается с глобальным
состоянием через useContext или сторонние менеджеры
состояния. Это позволяет централизованно хранить состояние ошибок и
управлять уведомлениями пользователя. Пример:
import { createContext } from 'preact';
import { useContext } from 'preact/hooks';
const ErrorContext = createContext();
function ErrorProvider({ children }) {
const [error, setError] = useState(null);
return (
<ErrorContext.Provider value={{ error, setError }}>
{children}
</ErrorContext.Provider>
);
}
function useError() {
return useContext(ErrorContext);
}
Таким образом, компоненты могут обновлять и получать состояние ошибок без дублирования кода.
Обработка сетевых ошибок в Preact строится на четком разделении
состояний загрузки, данных и ошибок, использовании асинхронных блоков
try/catch, применении таймаутов и повторных попыток, а
также на возможности централизованного управления ошибками через
контекст или ErrorBoundary. Такой подход обеспечивает
стабильность интерфейса и предсказуемость поведения приложения в
условиях нестабильного соединения.