React 19.2: useOptimistic с Actions API для мгновенных обновлений задач и отката
-

React 19.2 приносит useOptimistic - хук, который мгновенно обновляет UI при действиях с серверами. Это решает проблему задержек в списках задач: задача добавляется сразу, а не ждет ответа от бэка. С Actions API откат при ошибке происходит автоматически - никаких ручных if’ов.
Для списков задач это киллер-фича: пользователь видит отзывчивость, а состояние синхронизируется без лишнего кода. Забудь про старые optimistic updates с useState и useEffect - теперь все чисто и декларативно. Погнали разбирать на примерах.
useOptimistic: суть и базовый синтаксис
Хук useOptimistic принимает текущее состояние и чистую функцию-обновлятор. Она описывает, как выглядит UI во время асинхронного действия. Пока сервер молчит, рендерится оптимистичная версия - например, новая задача в списке с меткой “sending”.
Возвращает хук пару: оптимистичное состояние и функцию addOptimistic для триггера. В Actions API это идеально ложится на form actions - вызываешь addOptimistic перед fetch, и UI оживает мгновенно. Если сервер вернул ошибку, React сам откатит к исходному стейту. Никаких try-catch в рендере.
const [optimisticTodos, addOptimisticTodo] = useOptimistic( todos, (state, newTodo) => [{ ...newTodo, isPending: true }, ...state] );Вот базовый workflow:
- Пользователь сабмитит форму добавления задачи.
- addOptimisticTodo(newTodo) обновляет список сразу.
- startTransition запускает реальный POST на сервер.
- При успехе стейт синхронизируется, isPending сбрасывается.
- При ошибке - автоматический роллбэк.
Actions API + useOptimistic для задач
Actions в React 19.2 - это server actions на клиенте, привязанные к формам через action={fn}. Они асинхронные по умолчанию, так что useOptimistic с ними - match made in heaven. Для списка задач: добавь задачу в UI мгновенно, сервер подтвердит - ок, нет - откат.
Представь типичный todo-лист: input, кнопка add, список. Без оптимизма - лагает на мобильном из-за сети. С хуком - кнопка живая, задача прыгает в список с индикатором, сервер отвечает за 300мс - готово. А если 404 - бац, и ее как не бывало.
Сценарий Без useOptimistic С useOptimistic + Actions Добавление задачи Ждем сеть, spinner Мгновенно + “pending” Ошибка сервера Ручной роллбэк Автооткат Удаление Лагает список Удаляется сразу Несколько действий useEffect hell Чистые dispatch’и Ключевые плюсы Actions:
- Нет нужды в useState для pending - хук сам управляет.
- startTransition предотвращает блокировку рендера.
- Поддержка нескольких optimistic updates в одном action.
Пример: todo-лист с добавлением и удалением
Строим реальный список задач. State - массив объектов {id, text, done, pending}. Form action добавляет optimistic todo, сервер сохраняет в DB. Удаление - то же самое, с роллбэком при ошибке.
В компоненте ThreadTodos используем useRef для формы, useOptimistic для стейта. Action вызывает addOptimistic перед fetch. Серверный action - async fn с реальным API.
import { useOptimistic, useRef, startTransition } from 'react'; function TodoList({ todos, addAction, deleteAction }) { const formRef = useRef(); const [optimisticTodos, addOptimistic] = useOptimistic(todos, (state, action) => { if (action.type === 'add') { return [{ id: Date.now(), ...action.payload, pending: true }, ...state]; } if (action.type === 'delete') { return state.filter(t => t.id !== action.payload.id); } return state; }); async function addTodo(formData) { const text = formData.get('text'); addOptimistic({ type: 'add', payload: { text } }); formRef.current.reset(); startTransition(async () => await addAction(formData)); } return ( <> <form action={addTodo} ref={formRef}> <input name="text" placeholder="Новая задача" /> <button>Добавить</button> </form> <ul> {optimisticTodos.map(todo => ( <li key={todo.id}> {todo.text} {todo.pending && <small> (синхронизация...)</small>} <button action={() => deleteAction(todo.id)}>Удалить</button> </li> ))} </ul> </> ); }Шаги реализации:
- Инициализируй state на сервере или useState([]).
- В action’е: addOptimistic перед await fetch.
- updateFn должна быть чистой - никаких сайд-эффектов.
- Для удаления: dispatch с типом ‘delete’.
Питфаллы и прод-трюки
Не все так гладко: updateFn не получает реальный стейт от сервера, только optimistic. Для сложных случаев комбинируй с React Query - onMutate с addOptimistic. На слабом железе startTransition обязателен, иначе фриз.
Частые ошибки:
- Забыл reset формы - юзер добавит дважды.
- Не чистая updateFn - баги в рендере.
- Нет key в списке - React не diff’ит.
// Прод-трюк: несколько updates addOptimistic({ type: 'optimisticUpdate', payload }); addOptimistic({ type: 'anotherUpdate' }); // Накладывается!Проблема Решение Когда использовать Конфликты стейта useOptimistic с reducer Много actions SSR + optimistic hydrateRoot с pending Next.js apps Тестирование Mock actions в vitest CI/CD Когда useOptimistic меняет игру
useOptimistic с Actions - это эволюция: от boilerplate’а с useSWR к нативному React. Осталось доработать мульти-тенант синхронизацию и WebSocket fallback’и для реал-тайм. В списках задач это уже делает UX на уровне Gmail - добавь, и готово.
Дальше думай про комбо с use() для промисов из серверных actions. Или optimistic в инфините-листах с виртуализацией - там лагов меньше некуда.
Здравствуйте! Похоже, вас заинтересовала эта беседа, но у вас ещё нет аккаунта.
Надоело каждый раз пролистывать одни и те же посты? Зарегистрировав аккаунт, вы всегда будете возвращаться на ту же страницу, где были раньше, и сможете выбирать, получать ли уведомления о новых ответах (по электронной почте или в виде push-уведомлений). Вы также сможете сохранять закладки и ставить лайки постам, чтобы выразить свою благодарность другим участникам сообщества.
С вашими комментариями этот пост мог бы стать ещё лучше 💗
Зарегистрироваться Войти© 2024 - 2026 ExLends, Inc. Все права защищены.