Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Suspense і завантаження даних». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`React.Suspense`** дозволяє відкласти рендер компонента, поки не будуть готові його дані, і показати fallback замість ручного стану `loading`. **Ключове:** React сам призупиняє рендер, коли компонент кидає `Promise`, показує fallback з найближчого `<Suspense>`, а коли проміс завершується, рендер відновлюється автоматично, без `useEffect` і `setState`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## 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) | Кеш + прапорці стану | Трохи складніше при композиції | | Suspense | React сам чекає дані і керує UI | Потребує сумісної бібліотеки (SWR, RQ, RSC) | --- ## ПІДСУМОК | Що робить Suspense при завантаженні даних | Чому це важливо | |---|---| | Призупиняє рендер, поки дані не готові | Уникає "порожнього" UI | | Показує fallback замість ручного loader'а | Спрощує код | | Відновлює рендер автоматично | Не потрібні `useEffect` і `setState` | | Працює з кількома джерелами даних | Дозволяє паралельне завантаження | | Інтегрується з concurrent rendering | Плавні переходи без блокування | | Використовується в Server Components | Потоковий SSR (Streaming) | --- ### Коротко: > `React.Suspense` **при завантаженні даних дозволяє React "чекати" дані асинхронно, показуючи fallback,** > **а потім автоматично продовжувати рендер, коли дані прийшли.** > > Це робить код простішим, інтерфейс плавнішим, а UX - миттєвим і передбачуваним.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.