ErrorBoundary і Suspense
Різниця між Suspense і Error Boundary
| Компонент | Що робить | Коли спрацьовує |
|---|---|---|
<Suspense> | Показує fallback, поки компонент ще не готовий (наприклад, іде завантаження даних або коду). | Коли компонент "призупиняється" (throws a Promise). |
<ErrorBoundary> | Показує fallback при помилці, коли компонент впав. | Коли компонент "впав" (throws an Error). |
React обробляє і
Promise, іErrorоднаково - через "викидання" (throw). Різниця в тому, що саме було викинуто:
Promise-> чекаємо (Suspense)Error-> показуємо помилку (ErrorBoundary)
Як вони працюють разом
React йде згори вниз:
- Компонент може "призупинити" рендер (кинути
Promise) -> React шукає найближчий<Suspense>. - Якщо компонент "впав" (кинув
Error) -> React шукає найближчий<ErrorBoundary>.
Тому їх можна і потрібно вкладати одне в одне, щоб різні ситуації (завантаження і помилка) оброблялися коректно.
Приклад правильного поєднання
import { Suspense } from "react";
import ErrorBoundary from "./ErrorBoundary";
import { UserProfile } from "./UserProfile";
function App() {
return (
<ErrorBoundary fallback={<h2>Error loading data</h2>}>
<Suspense fallback={<h2>Loading profile...</h2>}>
<UserProfile />
</Suspense>
</ErrorBoundary>
);
}Що станеться:
- якщо
UserProfileще не завантажився (наприклад,React.lazy()або Suspense-запит) -> покажеться fallback зSuspense("Loading..."); - якщо в
UserProfileстанеться помилка (наприклад,throw new Error()) -> покажеться fallback зErrorBoundary("Error loading data").
Приклад з React.lazy()
const UserProfile = React.lazy(() => import("./UserProfile"));Тут React.lazy() сам викидає Promise під час завантаження,
тому Suspense "підхопить" цей проміс і покаже свій fallback.
Приклад вкладених Suspense + ErrorBoundary
Ти можеш обгортати окремі ділянки UI:
function Dashboard() {
return (
<div>
<ErrorBoundary fallback={<h3>Error in stats</h3>}>
<Suspense fallback={<p>Loading stats...</p>}>
<StatsWidget />
</Suspense>
</ErrorBoundary>
<ErrorBoundary fallback={<h3>Error in profile</h3>}>
<Suspense fallback={<p>Loading profile...</p>}>
<ProfileWidget />
</Suspense>
</ErrorBoundary>
</div>
);
}Якщо StatsWidget ламається -> показується "Error in stats",
а ProfileWidget залишається недоторканим.
Якщо StatsWidget просто вантажиться -> "Loading stats...".
Усе незалежно.
Помилка новачків - навпаки вкласти
<Suspense fallback={<p>Loading...</p>}>
<ErrorBoundary fallback={<p>Error!</p>}>
<UserProfile />
</ErrorBoundary>
</Suspense>Так теж працює, але є нюанс: якщо помилка станеться до того, як Suspense відпрацює Promise, React може не встигнути показати "завантаження", а одразу покаже "помилку".
Тому зазвичай ErrorBoundary має бути "зовні", щоб зловити будь-які помилки всередині Suspense-зони. Хоча іноді (наприклад, для SSR або split-завантаження) - навпаки.
Резюме: хто за що відповідає
| Ситуація | Хто відпрацює | Що покаже |
|---|---|---|
| Компонент вантажиться (lazy, data) | Suspense | fallback-завантаження |
Компонент викинув помилку (throw new Error()) | ErrorBoundary | fallback-помилка |
| Помилка поза React (подія, async код) | - | потрібен try/catch вручну |
| Обидві ситуації можливі | Комбо | Suspense при завантаженні -> ErrorBoundary при помилці |
Рекомендований патерн (React 18+)
<ErrorBoundary fallback={<ErrorUI />}>
<Suspense fallback={<LoadingUI />}>
<AsyncComponent />
</Suspense>
</ErrorBoundary>Таким чином:
- Завантаження ->
LoadingUI - Помилка ->
ErrorUI - Успішний результат ->
AsyncComponent
Патерн для UI-зон (кращий підхід для великих застосунків)
| UI-зона | ErrorBoundary | Suspense |
|---|---|---|
| Головна сторінка | спільний ErrorBoundary | спільний Suspense |
| Окремі віджети | локальні ErrorBoundary | локальні Suspense |
| Критичні зони (checkout, auth) | окремі ErrorBoundary | Suspense для даних |
Підсумок
Так, ErrorBoundary і Suspense чудово працюють разом. Використовуй:
<Suspense>-> для завантаження (коли компонент ще не готовий),<ErrorBoundary>-> для помилок (коли компонент впав).
Рекомендований порядок вкладеності:
<ErrorBoundary>
<Suspense>
<Component />
</Suspense>
</ErrorBoundary>Такий патерн:
- ізолює помилки;
- показує fallback UI при завантаженні і збоях;
- робить інтерфейс стійким і плавним.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.