Skip to main content

Suspense і завантаження даних

1. Проблема без Suspense

У "класичному" React-застосунку завантаження даних виглядає так:

javascript
function Profile() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch('/api/user') .then(res => res.json()) .then(setUser) .finally(() => setLoading(false)); }, []); if (loading) return <p>Завантаження...</p>; return <h1>{user.name}</h1>; }

Недоліки такого підходу:

  • доводиться вручну керувати станами loading / error / data;
  • код сильно захаращується логікою;
  • UI "мигає" при кожному переході (видно loader);
  • React не може "призупинити" рендер і показати старі дані;
  • немає вбудованого способу синхронізувати кілька асинхронних компонентів.

2. Що робить Suspense

React.Suspense дозволяє відкласти рендер компонента, поки не будуть готові його дані, і показати fallback замість ручного стану loading.

Тобто React може "почекати" асинхронні дані сам, без потреби вручну писати useEffect і useState.


3. Як це працює концептуально

Коли React всередині дерева зустрічає компонент, який ще не готовий (наприклад, він кидає Promise), React:

  1. призупиняє рендер цієї ділянки дерева;
  2. показує fallback з найближчого <Suspense>;
  3. коли проміс завершується - рендер відновлюється автоматично.

4. Приклад з SWR і Suspense

Бібліотека SWR вміє працювати з Suspense "з коробки".

javascript
import useSWR from "swr"; import { Suspense } from "react"; const fetcher = (url) => fetch(url).then(res => res.json()); function UserProfile() { const { data } = useSWR("/api/user", fetcher, { suspense: true }); return <h1>Hello, {data.name}</h1>; } export default function App() { return ( <Suspense fallback={<p>Завантаження профілю...</p>}> <UserProfile /> </Suspense> ); }

Що відбувається:

  • useSWR при першому виклику кидає Promise, поки дані не прийшли;
  • React "ловить" цей Promise, зупиняє рендер;
  • показує fallback;
  • коли запит завершено -> React автоматично рендерить компонент заново з готовими даними.

Без loading, useEffect, setState - React сам "чекає".


5. Чому це круто

Менше ручного коду

Не потрібно вручну керувати loading / error.

Композиція асинхронних компонентів

Кілька компонентів всередині Suspense можуть одночасно чекати свої дані:

javascript
<Suspense fallback={<DashboardSkeleton />}> <UserProfile /> <UserStats /> <RecentActivities /> </Suspense>

React почекає, поки всі три отримають дані, і покаже їх одночасно -> без "мерехтіння" частинами.

Плавний UX

Старий контент залишається на екрані, React показує fallback тільки для нових даних, а не "очищає" весь екран.


6. Suspense + startTransition = плавні оновлення

React 18 представив concurrent rendering, і тепер Suspense можна використовувати для плавних переходів даних.

javascript
import { useState, Suspense, startTransition } from "react"; import useSWR from "swr"; function SearchResults({ query }) { const { data } = useSWR(`/api/search?q=${query}`, fetcher, { suspense: true }); return <ul>{data.results.map(r => <li key={r}>{r}</li>)}</ul>; } export function Search() { const [query, setQuery] = useState(''); return ( <> <input value={query} onChange={e => { const value = e.target.value; startTransition(() => setQuery(value)); }} placeholder="Search..." /> <Suspense fallback={<p>Loading...</p>}> <SearchResults query={query} /> </Suspense> </> ); }

Що робить React:

  • не блокує ввід (startTransition);
  • під капотом призупиняє оновлення SearchResults, поки дані не прийдуть;
  • показує fallback тільки для "нового стану".

У результаті: інтерфейс чуйний, пошук плавний, без мигань і затримок.


7. Suspense і React Server Components (Next.js 13+)

На сервері (App Router у Next.js) Suspense використовується для потокового рендера (Streaming SSR).

javascript
// app/page.tsx export default async function Page() { return ( <> <Header /> <Suspense fallback={<PostsSkeleton />}> <Posts /> {/* async-компонент */} </Suspense> </> ); }

Тут React:

  • рендерить сторінку на сервері;
  • одразу надсилає користувачу готові частини (Header, layout);
  • пізніше "добудовує" решту, коли Posts завантажиться.

UX - як у SPA, але з SSR: сторінка з'являється миттєво, дані підвантажуються частинами.


8. Порівняння підходів

ПідхідЩо робитьМінуси
useEffect + useStateРучне керування завантаженнямДублювання коду, "мигання" UI
SWR / React Query (без Suspense)Кеш + прапорці стануТрохи складніше при композиції
SuspenseReact сам чекає дані і керує UIПотребує сумісної бібліотеки (SWR, RQ, RSC)

ПІДСУМОК

Що робить Suspense при завантаженні данихЧому це важливо
Призупиняє рендер, поки дані не готовіУникає "порожнього" UI
Показує fallback замість ручного loader'аСпрощує код
Відновлює рендер автоматичноНе потрібні useEffect і setState
Працює з кількома джерелами данихДозволяє паралельне завантаження
Інтегрується з concurrent renderingПлавні переходи без блокування
Використовується в Server ComponentsПотоковий SSR (Streaming)

Коротко:

React.Suspense при завантаженні даних дозволяє React "чекати" дані асинхронно, показуючи fallback, а потім автоматично продовжувати рендер, коли дані прийшли.

Це робить код простішим, інтерфейс плавнішим, а UX - миттєвим і передбачуваним.

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

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

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