В чем разница между componentDidCatch и getDerivedStateFromError?
Комментарии (3)
Ответ сгенерирован нейросетью и может содержать ошибки
Error Boundaries: componentDidCatch vs getDerivedStateFromError
Это два метода в Error Boundary (class компоненты), которые отлавливают ошибки в React приложении. Они работают на разных этапах и имеют разные цели.
Краткое резюме
| Метод | Этап | Для чего |
|---|---|---|
| getDerivedStateFromError | Render | Обновить state, чтобы отрисовать fallback UI |
| componentDidCatch | После render | Логирование, отправка в error tracking |
getDerivedStateFromError: управление UI
Это статический метод, вызываемый во время render фазы:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
// Вызывается во время render (у компонента, который выбросил ошибку)
static getDerivedStateFromError(error) {
// СИНХРОННЫЙ метод
// Обновляем state, чтобы отрисовать fallback UI
return { hasError: true, error };
}
render() {
if (this.state.hasError) {
return (
<div className="error-container">
<h1>Упс, что-то пошло не так</h1>
<p>{this.state.error?.message}</p>
<button onClick={() => this.setState({ hasError: false })}>
Попробовать снова
</button>
</div>
);
}
return this.props.children;
}
}
Характеристики getDerivedStateFromError:
- Статический метод (нет доступа к this)
- Синхронный (блокирует render)
- Вызывается во время render фазы
- Используется для обновления state
- Возвращает новый state, который будет merged
- НЕЛЬЗЯ здесь логировать или делать побочные эффекты
componentDidCatch: логирование и побочные эффекты
Это нормальный метод, вызываемый ПОСЛЕ render и commit:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
// Вызывается после render (commit фаза)
componentDidCatch(error, errorInfo) {
// Асинхронный, побочные эффекты разрешены
// 1. Логирование
console.error('Error caught:', error, errorInfo);
// 2. Отправка в error tracking (Sentry, LogRocket)
Sentry.captureException(error, {
contexts: {
react: {
componentStack: errorInfo.componentStack
}
}
});
// 3. Отправка на сервер
fetch('/api/errors', {
method: 'POST',
body: JSON.stringify({
message: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
timestamp: new Date().toISOString()
})
});
}
render() {
if (this.state.hasError) {
return <div>Error UI</div>;
}
return this.props.children;
}
}
Характеристики componentDidCatch:
- Обычный метод (имеет доступ к this)
- Асинхронный (не блокирует render)
- Вызывается ПОСЛЕ render и commit
- Используется для логирования и побочных эффектов
- Параметры: error и errorInfo
- errorInfo.componentStack показывает полный стек ошибки
Практическая разница
// Полный пример Error Boundary
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// СИНХРОННО обновляем UI
console.log('getDerivedStateFromError called (синхронно)');
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// АСИНХРОННО логируем
console.log('componentDidCatch called (асинхронно)');
this.setState({ errorInfo });
// Отправляем на сервер (асинхронно)
this.logErrorToService(error, errorInfo);
}
logErrorToService = async (error, errorInfo) => {
try {
await fetch('/api/errors', {
method: 'POST',
body: JSON.stringify({
error: error.toString(),
stack: errorInfo.componentStack
})
});
} catch (e) {
console.error('Failed to log error:', e);
}
}
render() {
if (this.state.hasError) {
return (
<div className="error-boundary">
<h1>Кажется, произошла ошибка</h1>
{process.env.NODE_ENV === 'development' && (
<details style={{ whiteSpace: 'pre-wrap' }}>
<summary>Детали ошибки</summary>
<p>{this.state.error?.toString()}</p>
<p>{this.state.errorInfo?.componentStack}</p>
</details>
)}
</div>
);
}
return this.props.children;
}
}
Когда ошибка ловится
// Ловятся (Error Boundary поймает):
- Render методы
- Lifecycle методы (componentDidMount, etc.)
- Конструкторы
- setState callback
// НЕ ловятся (Event handlers, async code):
- Event handlers (нужно try-catch)
- setTimeout / async code
- Promise rejection
- SSR
Пример с Event Handler:
class MyComponent extends React.Component {
handleClick = async () => {
try {
// Error Boundary НЕ поймает эту ошибку!
throw new Error('Клик ошибка');
} catch (error) {
// Нужно self handle
this.setState({ error });
}
}
render() {
return <button onClick={this.handleClick}>Клик</button>;
}
}
Лучшие практики
1. Используй оба метода вместе
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Логирование ПОСЛЕ того как UI обновлён
logError(error, errorInfo);
}
2. Не логируй в getDerivedStateFromError
// ❌ НЕПРАВИЛЬНО
static getDerivedStateFromError(error) {
console.log(error); // Может быть вызвано несколько раз
Sentry.captureException(error); // В миддле render?
return { hasError: true };
}
// ✅ ПРАВИЛЬНО
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.log(error);
Sentry.captureException(error);
}
3. Разные Error Boundaries для разных частей
function App() {
return (
<div>
<ErrorBoundary>
<Header />
</ErrorBoundary>
<ErrorBoundary>
<MainContent />
</ErrorBoundary>
<ErrorBoundary>
<Footer />
</ErrorBoundary>
</div>
);
}
// Если Header упадёт, Footer и MainContent всё ещё работают
4. Используй Sentry или LogRocket
class ErrorBoundary extends React.Component {
componentDidCatch(error, errorInfo) {
// Профессиональное логирование
Sentry.withScope(scope => {
scope.setContext('react', {
componentStack: errorInfo.componentStack
});
Sentry.captureException(error);
});
}
render() {
if (this.state.hasError) {
return <div>Что-то пошло не так</div>;
}
return this.props.children;
}
}
React 19: Error Boundary в функциональных компонентах
В React 19 добавлена поддержка error boundaries в функциональных компонентах через API (в разработке):
// В будущем (React 19+)
function MyErrorBoundary() {
return useErrorHandler();
}
А пока используем class компоненты или библиотеки типа react-error-boundary.
Вывод
getDerivedStateFromError:
- Синхронный
- Обновляет state для UI
- Вызывается ВО ВРЕМЯ render
- Для управления fallback UI
componentDidCatch:
- Асинхронный
- Логирование и побочные эффекты
- Вызывается ПОСЛЕ render
- Для отправки на error tracking
Оба необходимы для полноценной обработки ошибок в React приложении!