← Назад к вопросам
Как отловить и показать на экране ошибку в React?
2.3 Middle🔥 221 комментариев
#React
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как отловить и показать на экране ошибку в React
Типы ошибок в React
- Render ошибки — во время рендеринга компонента
- Event handler ошибки — в обработчиках событий (onClick, onChange)
- Async ошибки — в async/await и Promises
- Lifecycle ошибки — в useEffect и других хуках
Способ 1: Error Boundaries (class компоненты)
Error Boundary — это класс-компонент, который ловит ошибки в дочерних компонентах.
import React from "react";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// Обновить state чтобы следующий render показал fallback UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Залогировать ошибку
console.error("Error caught:", error, errorInfo);
this.setState({
error: error,
errorInfo: errorInfo
});
// Можно отправить ошибку на сервер
logErrorToServer(error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div style={{
padding: "20px",
backgroundColor: "#fee",
border: "1px solid #f00",
borderRadius: "4px"
}}>
<h2>Что-то пошло не так</h2>
<p>{this.state.error && this.state.error.toString()}</p>
<details style={{ whiteSpace: "pre-wrap", marginTop: "10px" }}>
{this.state.errorInfo && this.state.errorInfo.componentStack}
</details>
<button onClick={() => window.location.reload()}>
Перезагрузить страницу
</button>
</div>
);
}
return this.props.children;
}
}
// Использование
<ErrorBoundary>
<App />
</ErrorBoundary>
Что ловит Error Boundary:
- Ошибки в render методах
- Ошибки в lifecycle методах (constructor, componentDidMount, etc)
- Ошибки в построении дочерних компонентов
Что НЕ ловит:
- Event handlers (используй try-catch)
- Async код (setTimeout, Promises)
- Код самого Error Boundary
- Ошибки на сервере (SSR)
Способ 2: Try-Catch в event handlers
Error Boundaries не ловят ошибки в event handlers. Нужно использовать обычный try-catch.
function UserForm() {
const [error, setError] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
setError(null); // Очистить старые ошибки
try {
const response = await fetch("/api/users", {
method: "POST",
body: JSON.stringify({ name: "Alice" })
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log("Success:", data);
// Показать успех пользователю
} catch (error) {
console.error("Error:", error);
setError(error.message); // Показать ошибку
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Имя" />
<button type="submit">Отправить</button>
</form>
{error && (
<div style={{
padding: "10px",
backgroundColor: "#fee",
color: "#c00",
marginTop: "10px",
borderRadius: "4px"
}}>
Ошибка: {error}
</div>
)}
</div>
);
}
Способ 3: Ловля ошибок в useEffect
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let isMounted = true; // Для отмены setState после unmount
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch("/api/data");
if (!response.ok) {
throw new Error("Failed to fetch");
}
const json = await response.json();
if (isMounted) {
setData(json);
setError(null);
}
} catch (err) {
if (isMounted) {
setError(err.message);
setData(null);
}
} finally {
if (isMounted) {
setLoading(false);
}
}
};
fetchData();
return () => {
isMounted = false; // Cleanup
};
}, []);
if (loading) return <p>Загрузка...</p>;
if (error) return <p style={{ color: "red" }}>Ошибка: {error}</p>;
return <div>{JSON.stringify(data)}</div>;
}
Способ 4: Global error handler + Error Boundary
Комбинированный подход для production:
// 1. Error service — логирование ошибок
class ErrorService {
static log(error, context = {}) {
const errorData = {
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
context: context,
userAgent: navigator.userAgent
};
// Отправить на сервер (Sentry, LogRocket, etc)
console.error("Error logged:", errorData);
fetch("/api/logs/errors", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(errorData)
}).catch(console.error);
}
}
// 2. Error Boundary с логированием
class ErrorBoundary extends React.Component {
componentDidCatch(error, errorInfo) {
ErrorService.log(error, { type: "render", componentStack: errorInfo.componentStack });
}
// ... остальной код
}
// 3. Global handler для uncaught errors
window.addEventListener("error", (event) => {
ErrorService.log(event.error, { type: "global" });
});
// 4. Handler для unhandled rejections (Promise)
window.addEventListener("unhandledrejection", (event) => {
ErrorService.log(event.reason, { type: "unhandledRejection" });
});
// 5. Wrap async handlers
function withErrorHandler(asyncFn) {
return async (...args) => {
try {
return await asyncFn(...args);
} catch (error) {
ErrorService.log(error, { type: "eventHandler" });
throw error; // Пробросить дальше, если нужно
}
};
}
// Использование
const handleClick = withErrorHandler(async () => {
const response = await fetch("/api/data");
if (!response.ok) throw new Error("Request failed");
return response.json();
});
Способ 5: Custom Error UI компонент
function ErrorAlert({ error, onDismiss, retry }) {
if (!error) return null;
return (
<div style={{
padding: "16px",
backgroundColor: "#fee",
border: "1px solid #f00",
borderRadius: "4px",
marginBottom: "16px",
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}}>
<div>
<strong>Ошибка:</strong> {error.message || "Что-то пошло не так"}
</div>
<div>
{retry && (
<button
onClick={retry}
style={{ marginRight: "8px", cursor: "pointer" }}
>
Повторить
</button>
)}
{onDismiss && (
<button
onClick={onDismiss}
style={{ cursor: "pointer" }}
>
Закрыть
</button>
)}
</div>
</div>
);
}
// Использование
function App() {
const [error, setError] = useState(null);
const handleRetry = async () => {
try {
// Повторить запрос
await fetch("/api/data");
setError(null);
} catch (err) {
setError(err);
}
};
return (
<div>
<ErrorAlert
error={error}
onDismiss={() => setError(null)}
retry={handleRetry}
/>
{/* Остальной контент */}
</div>
);
}
Практический пример: Полный обработчик ошибок
import React, { useState } from "react";
function LoginForm() {
const [formState, setFormState] = useState({
email: "",
password: "",
loading: false,
error: null,
success: false
});
const handleSubmit = async (e) => {
e.preventDefault();
setFormState(prev => ({ ...prev, loading: true, error: null }));
try {
const response = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: formState.email,
password: formState.password
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || "Login failed");
}
const data = await response.json();
localStorage.setItem("token", data.token);
setFormState(prev => ({
...prev,
loading: false,
success: true,
error: null
}));
// Перенаправить на главную
window.location.href = "/dashboard";
} catch (error) {
setFormState(prev => ({
...prev,
loading: false,
error: error.message
}));
}
};
return (
<form onSubmit={handleSubmit}>
{formState.error && (
<div style={{ color: "red", marginBottom: "10px" }}>
{formState.error}
</div>
)}
<input
type="email"
value={formState.email}
onChange={(e) => setFormState(prev => ({
...prev,
email: e.target.value
}))}
placeholder="Email"
/>
<input
type="password"
value={formState.password}
onChange={(e) => setFormState(prev => ({
...prev,
password: e.target.value
}))}
placeholder="Password"
/>
<button
type="submit"
disabled={formState.loading}
>
{formState.loading ? "Вход..." : "Войти"}
</button>
{formState.success && (
<p style={{ color: "green" }}>Вход успешен!</p>
)}
</form>
);
}
Лучшие практики
- Используй Error Boundaries для ловли render ошибок
- Используй try-catch в event handlers и async функциях
- Логируй ошибки на сервер для мониторинга
- Показывай friendly сообщения пользователю
- Предоставляй способ recover (retry, refresh)
- Обрабатывай loading и error состояния отдельно
Вывод
Полная стратегия обработки ошибок в React:
- Error Boundaries — для ошибок в render
- Try-catch — для async и event handlers
- Error состояние — для UI feedback
- Логирование — для мониторинга в production
- Global handlers — для unhandled ошибок
Это обеспечивает надежное приложение, которое gracefully обрабатывает ошибки.