React Suspense та Майбутнє
React Suspense та Майбутнє
React 18 змінив правила гри. Suspense більше не експериментальна фіча. Це рекомендований спосіб роботи з асинхронністю.
TanStack Query v5 має першокласну підтримку Suspense.
useSuspenseQuery
Раніше ми писали так:
function Profile() {
const { data, isLoading, isError } = useQuery(...);
if (isLoading) return <Spinner />;
if (isError) return <Error />;
// Тут data може бути undefined, тому треба перевіряти
return <div>{data?.name}</div>;
}
З useSuspenseQuery ми пишемо так:
function Profile() {
const { data } = useSuspenseQuery(...);
// data ЗАВЖДИ визначена тут.
// Якщо йде завантаження — компонент просто "зупиняється" (suspend).
// React ловить це і показує найближчий Suspense fallback.
return <div>{data.name}</div>;
}
Переваги
- Чистіший код: Ніяких
if (isLoading). - TypeScript:
dataтипізована якTData, а неTData | undefined. - Deklarative Loading: Ви керуєте спіннерами в батьківському компоненті, а не в кожному маленькому компоненті.
Структура Додатку
import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function App() {
return (
// 2. Якщо впаде помилка — покажеться це
<ErrorBoundary fallback={<div>Something went wrong</div>}>
{/* 1. Поки вантажиться — покажеться це */}
<Suspense fallback={<div>Loading Profile...</div>}>
<Profile />
<Posts />
</Suspense>
</ErrorBoundary>
);
}
Ви можете вкладати Suspense один в одного, створюючи гранулярний UI завантаження.
Transitions (Плавні переходи)
Суспенс має одну особливість: якщо ви змінюєте ключ запиту, старий UI зникає, і з'являється fallback (спіннер).
Іноді ми хочемо залишити старий UI, поки вантажиться новий (як в Instagram при переході між табами).
Для цього використовується useTransition.
function TabNav() {
const [tab, setTab] = useState('posts');
const [isPending, startTransition] = useTransition();
const handleClick = (newTab) => {
// Кажемо React: "Це оновлення не термінове.
// Якщо воно спровокує Suspense, не ховай поточний UI, а просто почекай".
startTransition(() => {
setTab(newTab);
});
};
return (
<div>
<div style={{ opacity: isPending ? 0.5 : 1 }}>
<button onClick={() => handleClick('posts')}>Posts</button>
<button onClick={() => handleClick('photos')}>Photos</button>
</div>
<Suspense fallback={<Spinner />}>
{tab === 'posts' ? <Posts /> : <Photos />}
</Suspense>
</div>
);
}
useSuspenseInfiniteQuery
Так само існує версія для нескінченного скролу.
Коли НЕ використовувати Suspense?
Suspense — це блокуючий механізм. Поки дані не завантажаться, React не покаже нічого (крім фолбека).
Якщо вам потрібен "background refetch" (показати старі дані, поки вантажаться нові), то звичайний useQuery з placeholderData може бути кращим варіантом.
Але для першого завантаження — Suspense перемагає.
Аутентифікація та Обробка Помилок
Робота з захищеними даними (Authorized only) додає шар складності. Що робити, якщо токен протух? Як глобально показати помилку?
Глибоке Занурення в Продуктивність
TanStack Query надзвичайно швидкий "з коробки". Але якщо ви рендерите тисячі елементів або маєте складні дані, варто розуміти, як він працює під капотом.