Что такое ErrorBoundary?

🎯 Зачем спрашивают
 
📝 Ответ
Коротко:
Error Boundary — это React-компонент, который перехватывает ошибки в своем поддереве и отображает запасной UI вместо падения всего приложения.
 
Ловит следующие типы ошибок:
  • Ошибки при рендере дочерних компонентов.
  • Ошибки в конструкторах этих компонентов.
  • Ошибки в методах жизненного цикла.
 
💡
Пока что для ErrorBoundary требуется использовать классовый компонент…
На сегодня (React 18) нет официального API для функциональных компонентов.
Однако есть кастомные решения (хуки вроде react-error-boundary от Kent C. Dodds).
Пример:
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // Обновляем state, чтобы показать fallback UI return { hasError: true }; } componentDidCatch(error, info) { // Логируем ошибку (например, в Sentry) console.error("Error caught by ErrorBoundary:", error, info); } render() { if (this.state.hasError) { return <h2>Что-то пошло не так 😢</h2>; } return this.props.children; } }
Использование:
<ErrorBoundary> <MyProblematicComponent /> </ErrorBoundary>
Подробнее: Самый частый use-case — добавить в приложение глобальный ErrorBoundary с логированием ошибок в bug tracker систему, например, Sentry.
👉 Пример
import React, { ErrorInfo, ReactNode } from 'react'; import * as Sentry from '@sentry/react'; import { WRONG_SESSION } from '../constants'; import { ErrorScreen } from '../ErrorScreen'; import { RefreshPageScreen } from '../RefreshPageScreen'; type Props = { children: ReactNode; }; type State = { error: Error | null; errorInfo: ErrorInfo | null; }; export class ErrorBoundary extends React.Component<Props, State> { constructor(props: Props) { super(props); this.state = { error: null, errorInfo: null }; } static getDerivedStateFromError(error: Error) { return { error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { Sentry.captureException(error, { extra: { componentStack: errorInfo.componentStack } }); this.setState({ error, errorInfo }); } render() { if (this.state.error) { if (this.state.error.message === WRONG_SESSION) { return <RefreshPageScreen />; } return <ErrorScreen />; } return <>{this.props.children}</>; } }
Использование:
<ErrorBoundary> <App /> </ErrorBoundary>
Тем не менее, вы можете использовать столько ErrorBoundary, сколько вам заблагорассудится. Ограничений нет.
 
Важно учесть, что ErrorBoundary не ловит:
  • ошибки внутри самого error boundary,
  • ошибки в асинхронных коллбэках (setTimeout, промисы),
  • ошибки в event handlers.
С помощью глобального ErrorBoundary можно отловить ошибки асинхронного кода:
import React, { ErrorInfo, ReactNode } from 'react'; import * as Sentry from '@sentry/react'; import { WRONG_SESSION } from '../constants'; import { ErrorScreen } from '../ErrorScreen'; import { RefreshPageScreen } from '../RefreshPageScreen'; type Props = { children: ReactNode; }; type State = { error: Error | null; errorInfo: ErrorInfo | null; }; export class ErrorBoundary extends React.Component<Props, State> { constructor(props: Props) { super(props); this.state = { error: null, errorInfo: null }; } static getDerivedStateFromError(error: Error) { return { error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { Sentry.captureException(error, { extra: { componentStack: errorInfo.componentStack } }); this.setState({ error, errorInfo }); } // По умолчанию ErrorBoundary не ловит async-ошибки, но // можно дописать собственную обёртку private handlePromiseRejection = (event: PromiseRejectionEvent) => { this.setState({ error: event.reason }); }; componentDidMount() { window.addEventListener('unhandledrejection', this.handlePromiseRejection); } componentWillUnmount() { window.removeEventListener('unhandledrejection', this.handlePromiseRejection); } render() { if (this.state.error) { if (this.state.error.message === WRONG_SESSION) { return <RefreshPageScreen />; } return <ErrorScreen />; } return <>{this.props.children}</>; } }
Но это, скорее, последняя линяя обороны.
 
⚖️ Компромиссы
 
🔎 Встречные вопросы
  • Какие ошибки ErrorBoundary не ловит?
  • Когда использовать один глобальный boundary, а когда локальные?
  • Как тестировать ErrorBoundary?
 
🚩 Красные флаги
  • ErrorBoundary ловит все ошибки в приложении (неправда).
  • Лучше использовать только один глобальный ErrorBoundary (не всегда верно).
  • Нужен для логирования ошибок, UI fallback не обязателен (частично, но fallback всегда нужен).
 
🛠 Практика
  • Сделать компонент с кнопкой, которая кидает исключение → показать, что без ErrorBoundary падает всё приложение, а с ним — только компонент.
  • Реализовать два ErrorBoundary: один глобальный, один локальный для «проблемной» зоны.
 
📚 Источники / ссылки