Skip to main content
Практика завдань

Границі помилок у React

Що таке Error Boundaries?

Error Boundaries — це компоненти React, які ловлять помилки JavaScript у дереві компонентів на своїх дочірніх компонентх, записують їх у лог та відображають резервний інтерфейс замість того, щоб зламати всю програму.


Чому варто використовувати Error Boundaries?

До появи Error Boundaries, помилка в одному компоненті могла зламати всю програму. Error Boundaries дозволяють вам:

  • Запобігти зламу всієї програми
  • Показати користувачам дружнє повідомлення про помилку
  • Записувати помилки для аналізу
  • Ізолювати проблемні частини програми

Створення Error Boundary

Error Boundary — це класовий компонент з методами getDerivedStateFromError та/або componentDidCatch.

javascript
import React from 'react'; class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // Оновити стан, щоб показати резервний інтерфейс return { hasError: true }; } componentDidCatch(error, errorInfo) { // Записати помилку в сервіс звітності console.error('Помилка, спіймана кордоном:', error, errorInfo); } render() { if (this.state.hasError) { return <h1>Щось пішло не так.</h1>; } return this.props.children; } }

Використання

Обгорніть компоненти, які можуть викликати помилки:

javascript
function App() { return ( <div> <ErrorBoundary> <MyWidget /> </ErrorBoundary> <ErrorBoundary> <AnotherWidget /> </ErrorBoundary> </div> ); }

Якщо MyWidget викликає помилку, тільки він зламається, тоді як AnotherWidget продовжить працювати.


Різниця між методами

getDerivedStateFromError

Викликається під час фази рендерингу. Використовується для оновлення стану та показу резервного інтерфейсу.

javascript
static getDerivedStateFromError(error) { // Потрібно повернути об'єкт для оновлення стану return { hasError: true, error }; }

componentDidCatch

Викликається після рендерингу. Використовується для запису помилок.

javascript
componentDidCatch(error, errorInfo) { // errorInfo.componentStack містить стек викликів компонентів logErrorToService(error, errorInfo); }

Розширений приклад

javascript
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, error: null, errorInfo: null }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, errorInfo) { this.setState({ error, errorInfo }); // Відправити в сервіс моніторингу logErrorToService(error, errorInfo); } handleReset = () => { this.setState({ hasError: false, error: null, errorInfo: null }); }; render() { if (this.state.hasError) { return ( <div className="error-container"> <h2Виникла помилка</h2> <details> <summary>Деталі</summary> <p>{this.state.error && this.state.error.toString()}</p> <pre>{this.state.errorInfo && this.state.errorInfo.componentStack}</pre> </details> <button onClick={this.handleReset}> Спробувати ще раз </button> </div> ); } return this.props.children; } }

Що не ловлять Error Boundaries

Error Boundaries ловлять помилки в:

  • Методах рендерингу
  • Життєвих циклах
  • Конструкторах дочірніх компонентів

Вони НЕ ловлять:

  • Обробники подій
  • Асинхронний код (setTimeout, Promise)
  • Помилки на серверній стороні
  • Помилки в самому Error Boundary

Обробники подій

Використовуйте звичайний try-catch для обробників подій:

javascript
function MyComponent() { function handleClick() { try { // Код, який може викликати помилку somethingDangerous(); } catch (error) { console.error('Помилка в обробнику кліків:', error); } } return <button onClick={handleClick}>Натисни на мене</button>; }

Асинхронні помилки

Використовуйте try-catch для асинхронних помилок також:

javascript
function MyComponent() { async function fetchData() { try { const response = await fetch('/api/data'); const data = await response.json(); // ... } catch (error) { console.error('Помилка при отриманні:', error); } } useEffect(() => { fetchData(); }, []); return <div>...</div>; }

Де розміщувати Error Boundaries

Глобальний рівень

javascript
function App() { return ( <ErrorBoundary> <Router> <Layout> <Routes /> </Layout> </Router> </ErrorBoundary> ); }

Рівень маршруту

javascript
function App() { return ( <Router> <ErrorBoundary> <Route path="/profile" element={<Profile />} /> </ErrorBoundary> <ErrorBoundary> <Route path="/settings" element={<Settings />} /> </ErrorBoundary> </Router> ); }

Рівень компонент

javascript
function Dashboard() { return ( <div> <ErrorBoundary> <ChartWidget /> </ErrorBoundary> <ErrorBoundary> <StatsWidget /> </ErrorBoundary> </div> ); }

Інтеграція з сервісами моніторингу

Sentry

javascript
import * as Sentry from '@sentry/react'; class ErrorBoundary extends React.Component { componentDidCatch(error, errorInfo) { Sentry.captureException(error, { contexts: { react: errorInfo } }); } // ... } // Або використовуйте вбудований ErrorBoundary від Sentry import { ErrorBoundary } from '@sentry/react'; function App() { return ( <ErrorBoundary fallback={<ErrorFallback />}> <MyApp /> </ErrorBoundary> ); }

React 18 та useErrorBoundary

React ще не має хуку для Error Boundaries, але бібліотеки пропонують рішення:

react-error-boundary

javascript
import { ErrorBoundary, useErrorHandler } from 'react-error-boundary'; function ErrorFallback({ error, resetErrorBoundary }) { return ( <div role="alert"> <p>Щось пішло не так:</p> <pre>{error.message}</pre> <button onClick={resetErrorBoundary}>Спробувати ще раз</button> </div> ); } function App() { return ( <ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => { // Скинути стан програми }} onError={(error, errorInfo) => { // Записати помилку }} > <MyApp /> </ErrorBoundary> ); } // Використовуйте в функціональних компонентх function MyComponent() { const handleError = useErrorHandler(); async function fetchData() { try { const response = await fetch('/api/data'); if (!response.ok) throw new Error('Не вдалося отримати'); return response.json(); } catch (error) { handleError(error); } } // ... }

Кращі практики

Кілька Error Boundaries

Не використовуйте один глобальний Error Boundary. Використовуйте кілька на різних рівнях:

javascript
function App() { return ( <ErrorBoundary fallback={<GlobalError />}> <Header /> <ErrorBoundary fallback={<SidebarError />}> <Sidebar /> </ErrorBoundary> <ErrorBoundary fallback={<ContentError />}> <Content /> </ErrorBoundary> <Footer /> </ErrorBoundary> ); }

Інформативні повідомлення

Показуйте користувачам корисну інформацію:

javascript
function ErrorFallback({ error }) { return ( <div className="error-page"> <h1>Упс! Щось пішло не так</h1> <p>Ми працюємо над виправленням цього.</p> <p>Спробуйте:</p> <ul> <li>Оновити сторінку</li> <li>Повернутися на головну</li> <li>Повідомити нас про проблему</li> </ul> {process.env.NODE_ENV === 'development' && ( <details> <summary>Технічні деталі</summary> <pre>{error.message}</pre> </details> )} </div> ); }

Варіанти відновлення

Дайте користувачам можливість спробувати ще раз:

javascript
class ErrorBoundary extends React.Component { state = { hasError: false, errorCount: 0 }; static getDerivedStateFromError(error) { return { hasError: true }; } componentDidUpdate(prevProps) { // Скинути помилку, коли діти змінюються if (this.state.hasError && prevProps.children !== this.props.children) { this.setState({ hasError: false, errorCount: 0 }); } } handleReset = () => { this.setState(state => ({ hasError: false, errorCount: state.errorCount + 1 })); }; render() { if (this.state.hasError) { return ( <div> <h1>Помилка</h1> <button onClick={this.handleReset}>Спробувати ще раз</button> {this.state.errorCount > 2 && ( <p>Якщо проблема не зникне, будь ласка, зверніться до служби підтримки.</p> )} </div> ); } return this.props.children; } }

Висновок

Error Boundaries:

  • Ловлять помилки в компонентх React
  • Має бути класовими компонентми
  • Використовують getDerivedStateFromError та componentDidCatch
  • Не ловлять помилки в обробниках подій та асинхронному коді
  • Найкраще використовувати кілька на різних рівнях
  • Дозволяють показувати резервний інтерфейс замість порожнього екрану
  • Важливі для виробничих застосунків

На співбесідах:

Важливо вміти:

  • Пояснити, що таке Error Boundaries
  • Показати, як створити Error Boundary
  • Описати, які помилки вони не ловлять
  • Пояснити різницю між getDerivedStateFromError та componentDidCatch
  • Навести приклади правильного розміщення Error Boundary

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?
Практика завдань