<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Next.js 15: Streaming с useOptimistic и useActionState для динамических таблиц с пагинацией]]></title><description><![CDATA[<p dir="auto"><img src="/assets/uploads/files/ac/7e/2f/1773919909056-generated_1773919878718-resized.webp" alt="Обложка: Next.js 15: Streaming с React 19 useOptimistic и useActionState для динамических таблиц с пагинацией" class=" img-fluid img-markdown" /></p>
<p dir="auto">В Next.js 15 с React 19 динамические таблицы с пагинацией стали проще и быстрее благодаря <strong>streaming</strong>, <strong>useOptimistic</strong> и <strong>useActionState</strong>. Эти фичи решают классическую боль: задержки при загрузке данных, подвисания UI и плохие Web Vitals на больших таблицах. Мы разберём, как комбинировать их для реального DX.</p>
<p dir="auto">Пагинация в таблицах часто превращается в тормоза из-за полного ререндера страницы. Streaming позволяет рендерить скелетоны сразу, а useOptimistic даёт мгновенный optimistic update. useActionState упрощает обработку форм и ошибок без лишнего бойлерплеита. В итоге получаем плавный UX даже на слабом соединении.</p>
<h2>Streaming в Next.js 15: рендерим таблицу по кускам</h2>
<p dir="auto">Streaming в App Router - это киллер-фича для динамического контента. Вместо монолитного рендера сервера шлёт HTML по частям: сначала статический шелл, потом данные. В Next.js 15 это работает из коробки с <strong>loading.tsx</strong> и Suspense. Для таблиц с пагинацией это идеально: скелетон таблицы мигает на миг, а строки подгружаются асинхронно.</p>
<p dir="auto">Представь таблицу заказов: 100+ строк, пагинация, фильтры. Без streaming пользователь видит белый экран 2-3 секунды. С loading.tsx статическая часть (хедеры, пагинация) летит первой, динамика - потом. React 19 усиливает это границами Suspense, где fallback - твой кастомный скелетон с shimmer-анимацией. DX огонь: меньше JS-бандла, лучше LCP.</p>
<ul>
<li><strong>loading.tsx на уровне страницы</strong>: Автоматически оборачивает контент в Suspense. Импортируй <code>&lt;TableSkeleton /&gt;</code> - и готово.</li>
<li><strong>Компонентный Suspense</strong>: Оберни <code>&lt;PaginatedTable /&gt;</code> в <code>&lt;Suspense fallback={&lt;SkeletonRows /&gt;} /&gt;</code>. Данные фетчи внутри компонента.</li>
<li><strong>Плюсы для пагинации</strong>: Каждая страница таблицы стримится независимо, нет блокировок.</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Сравнение рендера</th>
<th>Время LCP</th>
<th>JS-бандл</th>
<th>UX на мобильном</th>
</tr>
</thead>
<tbody>
<tr>
<td>Без streaming</td>
<td>3.2с</td>
<td>150кб</td>
<td>Тормоза</td>
</tr>
<tr>
<td>С streaming</td>
<td>0.8с</td>
<td>80кб</td>
<td>Плавно</td>
</tr>
</tbody>
</table>
<p dir="auto"><em>Нюанс: streaming работает только в Server Components. В Client - используй React.lazy.</em></p>
<h2>useOptimistic: мгновенные обновления без ожидания сервера</h2>
<p dir="auto">useOptimistic из React 19 - это хук для optimistic UI в формах. Ты мутируешь стейт локально на клиенте, сервер потом подтверждает или откатывает. Для таблиц с пагинацией это must-have: добавил строку? Она появляется сразу, без спиннера. Next.js 15 интегрирует это с Server Actions идеально.</p>
<p dir="auto">Допустим, таблица задач с пагинацией. Пользователь меняет статус задачи на “done”. useOptimistic обновляет строку в стейте мгновенно, серверная action асинхронно сохраняет в БД. Если ошибка - откат. Это решает фрустрацию от задержек API. В комбо со streaming таблица пагинируется без рефетча всей страницы.</p>
<p dir="auto">Вот короткий пример на TS:</p>
<pre><code class="language-tsx">function TaskTable({ initialTasks }: { initialTasks: Task[] }) {
  const [optimisticTasks, addOptimistic] = useOptimistic(
    initialTasks,
    (state, newTask: Task) =&gt; [...state, newTask]
  );

  async function addTask(formData: FormData) {
    const task = { id: crypto.randomUUID(), ...Object.fromEntries(formData) };
    addOptimistic(task);
    await createTask(task); // Server Action
  }

  return &lt;Table data={optimisticTasks} /&gt;;
}
</code></pre>
<ul>
<li><strong>Синтаксис</strong>: <code>useOptimistic(state, updateFn)</code>. updateFn получает текущий стейт + новое значение.</li>
<li><strong>Интеграция с формами</strong>: В action вызывай setter перед реальной логикой.</li>
<li><strong>Откат ошибок</strong>: Хук сам обрабатывает throw, возвращая исходный стейт.</li>
<li><strong>Для пагинации</strong>: Оптимистично добавляй/удаляй элементы в текущей странице.</li>
</ul>
<p dir="auto"><strong>Ключ: не мутируй оригинальный стейт - возвращай immutable копию.</strong></p>
<h2>useActionState: формы без лишнего стейта и спиннеров</h2>
<p dir="auto">useActionState - ещё один gem из React 19. Заменяет useFormStatus + ручной стейт для форм. Возвращает [state, action, isPending]. Идеально для пагинации таблиц: поиск, смена страницы, фильтры - всё в одной action. Next.js 15 делает Server Actions async по умолчанию, DX летает.</p>
<p dir="auto">В таблице с пагинацией форма поиска. Было: useState для query, useFormStatus для спиннера, ручной рефетч. Теперь: одна action обрабатывает submit, useActionState даёт pending и ошибки. Таблица стримится заново только при успехе. Меньше кода, меньше багов.</p>
<p dir="auto">Пример для пагинации:</p>
<pre><code class="language-tsx">import { useActionState } from 'react';

function PaginatedTable() {
  const [state, submit, pending] = useActionState(fetchPage, { data: [], error: null });

  return (
    &lt;&gt;
      &lt;form action={submit}&gt;
        &lt;input name="page" type="number" /&gt;
        &lt;button disabled={pending}&gt;{pending ? 'Загрузка...' : 'Далее'}&lt;/button&gt;
      &lt;/form&gt;
      &lt;Table data={state.data} /&gt;
    &lt;/&gt;
  );
}

async function fetchPage(prev: any, formData: FormData) {
  const page = formData.get('page');
  const data = await db.query('SELECT * FROM users LIMIT 20 OFFSET $1', [(Number(page) - 1) * 20]);
  return { data };
}
</code></pre>
<ul>
<li><strong>Триплет возврата</strong>: [state, dispatch, isPending] - всё, что нужно.</li>
<li><strong>Авто-рендер</strong>: pending триггерит ререндер с правильным UI.</li>
<li><strong>Ошибки в стейте</strong>: action кидает - state ловит как { error }.</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>useActionState vs старый подход</th>
<th>Код строк</th>
<th>Pending-хэндлинг</th>
<th>Ошибки</th>
</tr>
</thead>
<tbody>
<tr>
<td>Старый</td>
<td>25+</td>
<td>useFormStatus</td>
<td>try/catch</td>
</tr>
<tr>
<td>Новый</td>
<td>10</td>
<td>Встроено</td>
<td>В стейте</td>
</tr>
</tbody>
</table>
<p dir="auto"><em>Внимание: action должен быть async и принимать prevState.</em></p>
<h2>Комбо: таблица мечты с пагинацией на стероидах</h2>
<p dir="auto">Соберём всё вместе: streaming + useOptimistic + useActionState. Получаем таблицу, где пагинация мгновенная, обновления optimistic, ошибки не ломают UX. В Next.js 15 это работает на проде без Turbopack - просто мигрируй.</p>
<p dir="auto">Структура: page.tsx с loading.tsx, Table как Server Component с Suspense, Client-компонент с хуками для интерактива. Фильтры в форме с useActionState, добавление строк - useOptimistic. Web Vitals улетают в зелёный: CLS=0, FID&lt;10мс. Код чистый, типизированный TS.</p>
<p dir="auto"><strong>Готовый снippet для копипаста</strong>:</p>
<pre><code class="language-tsx">// app/users/page.tsx
async function UsersPage({ searchParams }: { searchParams: { page?: string } }) {
  const page = Number(searchParams.page ?? 1);
  const users = await db.getUsers(page);

  return &lt;PaginatedUsers users={users} /&gt;;
}

export default UsersPage;
</code></pre>
<h2>Почему это меняет правила игры в таблицах</h2>
<p dir="auto">Streaming с хуками React 19 делает динамические таблицы responsive как натив. Пагинация больше не bottleneck, optimistic UX убирает frustration. Но за кадром остались детали: интеграция с TanStack Table, caching в Next.js, edge-кейсы с infinite scroll. Если копать глубже - смотри на React Compiler для ещё большего буста.</p>
]]></description><link>https://forum.exlends.ru/topic/1759/next.js-15-streaming-s-useoptimistic-i-useactionstate-dlya-dinamicheskih-tablic-s-paginaciej</link><generator>RSS for Node</generator><lastBuildDate>Sat, 17 Oct 2026 03:52:37 GMT</lastBuildDate><atom:link href="https://forum.exlends.ru/topic/1759.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 19 Mar 2026 11:31:49 GMT</pubDate><ttl>60</ttl></channel></rss>