Изучи серверные компоненты, чтение промисов через use и оптимистичные обновления React 19.
Открыть этот урок в KodokonReact Server Components (RSC) заново проводят границу между клиентом и сервером. Серверный компонент выполняется только на сервере: его код никогда не попадает в клиентский бандл, он может быть async, напрямую читать базу данных или файловую систему, а его результат отправляется в браузер как сериализованный поток (RSC payload), который React объединяет с клиентским деревом. Взамен у него нет состояния, нет эффектов и нет доступа к браузеру: никакого useState, никакого useEffect, никаких обработчиков событий. На клиенте он никогда не рендерится повторно.
// Server component: never sent to the client.
export default async function NotesPage() {
const notes = await db.notes.findAll();
return (
<ul>
{notes.map((note) => (
<li key={note.id}>{note.title}</li>
))}
</ul>
);
}Интерактивность живёт в клиентских компонентах, помеченных директивой 'use client' в начале файла. Эта директива помечает не "клиентский компонент", а границу: всё, что импортирует этот модуль, тоже переезжает на клиентскую сторону. Пропы, пересекающие границу, должны быть сериализуемыми: простые объекты, массивы, промисы... но никогда обычные функции или экземпляры классов.
'use client';
import { useState } from 'react';
export function LikeButton({ initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<button onClick={() => setCount(count + 1)}>
{count} likes
</button>
);
}React 19 вводит use - API, который читает значение промиса или контекста. В отличие от хуков, use можно вызывать внутри условия или цикла. Получив промис, он приостанавливает компонент до разрешения, опираясь на ближайшую границу Suspense. Идиоматичный паттерн: серверный компонент создаёт промис, не дожидаясь его, передаёт его пропом, а клиентский компонент потребляет его через use: сервер начинает стримить страницу, не дожидаясь данных.
import { use, Suspense } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map((c) => (
<p key={c.id}>{c.text}</p>
));
}
function Page({ commentsPromise }) {
return (
<Suspense fallback={<Spinner />}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}Со стороны взаимодействия useTransition помечает обновление как несрочное и выставляет isPending, чтобы показать сдержанный индикатор, не блокируя ввод. useOptimistic дополняет этот дуэт для сетевых мутаций: ты сразу показываешь ожидаемое состояние, пока выполняется асинхронное действие; когда действие завершается (успешно или нет), React отбрасывает оптимистичное значение и снова показывает настоящее состояние, то, что идёт от источника истины.
const [optimisticLikes, addOptimisticLike] =
useOptimistic(likes, (count, delta) => count + delta);
function handleLike() {
startTransition(async () => {
addOptimisticLike(1);
await saveLike();
// At the end, React shows the real state again.
});
}use?useOptimistic, когда асинхронное действие завершается?