← Назад к вопросам
Как работать с ассинхронными данными в React Suspense?
2.3 Middle🔥 231 комментариев
#React#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работать с асинхронными данными в React Suspense
Suspense - это React компонент для обработки асинхронных операций (загрузка компонентов, данных). Он позволяет отложить рендеринг компонента, пока не будут загружены нужные данные, и показать fallback UI.
Основная концепция Suspense
import { Suspense } from 'react';
// Fallback UI которая показывается во время загрузки
function LoadingFallback() {
return <div>Loading data...</div>;
}
function MyComponent() {
return (
<Suspense fallback={<LoadingFallback />}>
<DataComponent />
</Suspense>
);
}
// DataComponent должен "throw promise" во время загрузки
Как работает Suspense изнутри
// Компонент может "throw" promise
// Это заставляет Suspense показать fallback
let cache = {};
let request = null;
function fetchData(url) {
// Если данные уже в кеше - вернуть их
if (url in cache) {
return cache[url];
}
// Если promise уже создан - вернуть его (для одновременных запросов)
if (request) {
throw request;
}
// Создать новый promise и throw его
request = fetch(url)
.then(res => res.json())
.then(data => {
cache[url] = data;
request = null;
return data;
})
.catch(err => {
request = null;
throw err;
});
throw request;
}
function UserProfile() {
// Это выполнится при каждом рендере
// Во время загрузки выбросит promise
const userData = fetchData('/api/user');
return <div>{userData.name}</div>;
}
// Использование
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserProfile />
</Suspense>
);
}
Правильный подход с use() хуком (React 19+)
import { use, Suspense } from 'react';
// Функция которая возвращает promise
function fetchUser(id) {
return fetch(`/api/users/${id}`)
.then(res => res.json());
}
function UserComponent({ userId }) {
// use() распаковывает promise
const user = use(fetchUser(userId));
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
function App() {
return (
<Suspense fallback={<div>Loading user...</div>}>
<UserComponent userId={1} />
</Suspense>
);
}
Несколько Suspense компонентов для каскадной загрузки
import { Suspense, use } from 'react';
function Header() {
const headerData = use(fetch('/api/header').then(r => r.json()));
return <header>{headerData.title}</header>;
}
function Content() {
const content = use(fetch('/api/content').then(r => r.json()));
return <main>{content.body}</main>;
}
function Sidebar() {
const sidebar = use(fetch('/api/sidebar').then(r => r.json()));
return <aside>{sidebar.info}</aside>;
}
function App() {
return (
<div>
{/* Header загружается и показывается отдельно */}
<Suspense fallback={<div>Loading header...</div>}>
<Header />
</Suspense>
<div style={{ display: 'flex' }}>
{/* Content загружается независимо */}
<Suspense fallback={<div>Loading content...</div>}>
<Content />
</Suspense>
{/* Sidebar загружается независимо */}
<Suspense fallback={<div>Loading sidebar...</div>}>
<Sidebar />
</Suspense>
</div>
</div>
);
}
Использование с Server Components (Next.js)
// app/user/[id]/page.tsx
import { Suspense } from 'react';
async function UserData({ userId }) {
// Это Server Component - можно использовать async
const res = await fetch(`/api/users/${userId}`, {
next: { revalidate: 60 }
});
const user = await res.json();
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
async function UserComments({ userId }) {
const res = await fetch(`/api/users/${userId}/comments`, {
next: { revalidate: 30 }
});
const comments = await res.json();
return (
<div>
{comments.map(comment => (
<div key={comment.id}>{comment.text}</div>
))}
</div>
);
}
export default function Page({ params }) {
return (
<div>
<Suspense fallback={<div>Loading user...</div>}>
<UserData userId={params.id} />
</Suspense>
<Suspense fallback={<div>Loading comments...</div>}>
<UserComments userId={params.id} />
</Suspense>
</div>
);
}
Обработка ошибок с Error Boundary
import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
function DataComponent() {
const data = use(fetchDataThatCanFail());
return <div>{data}</div>;
}
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Suspense fallback={<div>Loading...</div>}>
<DataComponent />
</Suspense>
</ErrorBoundary>
);
}
Паттерн с кешированием запросов
class QueryCache {
constructor() {
this.cache = new Map();
}
get(key, fn) {
if (this.cache.has(key)) {
return this.cache.get(key);
}
const promise = fn().catch(err => {
this.cache.delete(key);
throw err;
});
this.cache.set(key, promise);
return promise;
}
invalidate(key) {
this.cache.delete(key);
}
}
const cache = new QueryCache();
function useQuery(key, fn) {
return use(cache.get(key, fn));
}
function Users() {
const users = useQuery('users', () =>
fetch('/api/users').then(r => r.json())
);
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
function App() {
return (
<Suspense fallback={<div>Loading users...</div>}>
<Users />
</Suspense>
);
}
Lazy loading компонентов
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const AnotherComponent = lazy(() => import('./AnotherComponent'));
function App() {
const [showHeavy, setShowHeavy] = React.useState(false);
return (
<div>
<button onClick={() => setShowHeavy(!showHeavy)}>
Toggle Heavy Component
</button>
{showHeavy && (
<Suspense fallback={<div>Loading component...</div>}>
<HeavyComponent />
</Suspense>
)}
<Suspense fallback={<div>Loading...</div>}>
<AnotherComponent />
</Suspense>
</div>
);
}
Ключевые моменты
- Suspense ловит thrown promises от компонентов
- use() хук распаковывает promise в синхронные данные
- Несколько Suspense компонентов загружаются параллельно
- Fallback показывается пока promise не разрешится
- Можно сочетать с Error Boundary для обработки ошибок
- Server Components в Next.js работают отлично с Suspense
- Lazy loading компонентов работает через Suspense
- Кеширование запросов важно для оптимизации
Suspense - это мощный механизм для управления асинхронностью в React, который делает код более читаемым и управляемым.