Back

Что изменилось в асинхронном рендеринге в React 19

Что изменилось в асинхронном рендеринге в React 19

React 18 представил конкурентный рендеринг. React 19 не заменяет этот фундамент — он надстраивается над ним, предоставляя стандартизированные паттерны для асинхронных рабочих процессов, которые вы ранее реализовывали вручную.

Если вы управляли состояниями isPending, координировали обработку ошибок в асинхронных операциях или вручную реализовывали оптимистичные обновления, React 19 предоставляет для всего этого API первого класса. Эта статья объясняет, что именно изменилось и как должна измениться ваша ментальная модель.

Для справки: все обсуждаемые здесь API задокументированы в официальной документации React: https://react.dev

Ключевые выводы

  • React 19 надстраивается над конкурентным рендерингом React 18, добавляя высокоуровневые абстракции для распространённых асинхронных паттернов
  • startTransition теперь принимает асинхронные функции, которые в документации React формально называются Actions (действиями)
  • useActionState устраняет шаблонный код для обработки форм со встроенным управлением состоянием ожидания и ошибками
  • useOptimistic стандартизирует оптимистичные обновления с откатом, управляемым каноническим состоянием
  • API use() позволяет читать промисы во время рендеринга, но требует кешированных промисов или промисов, совместимых с Suspense

Основное изменение: от ручной настройки к встроенным паттернам

React 18 дал нам движок конкурентного рендеринга. React 19 даёт нам абстракции, которые делают его практичным.

Ранее обработка асинхронной отправки формы означала жонглирование несколькими переменными состояния:

const [isPending, setIsPending] = useState(false)
const [error, setError] = useState(null)

const handleSubmit = async () => {
  setIsPending(true)
  try {
    await submitData()
  } catch (e) {
    setError(e)
  } finally {
    setIsPending(false)
  }
}

Actions в React 19 полностью устраняют этот шаблонный код.

Actions и асинхронные переходы в React 19

Самое большое изменение заключается в том, что startTransition теперь принимает асинхронные функции. В React 19 эти асинхронные функции переходов формально называются Actions (действиями) в документации React.

const [isPending, startTransition] = useTransition()

const handleSubmit = () => {
  startTransition(async () => {
    const error = await updateName(name)
    if (error) {
      setError(error)
      return
    }
    // выполнить навигацию или обновление состояния при успехе
  })
}

React 19 отслеживает состояние ожидающего перехода за вас, которое вы затем явно читаете через API, такие как useTransition или useActionState. Все обновления состояния внутри startTransition группируются вместе, производя единственный ре-рендер, когда асинхронная работа завершается. Это позволяет избежать промежуточных состояний, где isPending равно false, но состояние ошибки ещё не применено.

useActionState: специализированная обработка форм

Специально для форм useActionState предоставляет встроенное управление состоянием ожидания и результатами:

const [error, submitAction, isPending] = useActionState(
  async (previousState, formData) => {
    const error = await updateName(formData.get("name"))
    if (error) return error
    // выполнить навигацию или обновить состояние приложения при успехе
    return null
  },
  null
)

return (
  <form action={submitAction}>
    <input type="text" name="name" />
    <button disabled={isPending}>Обновить</button>
    {error && <p>{error}</p>}
  </form>
)

React теперь расширяет нативный атрибут action формы интегрированной обработкой состояния ожидания и результатов.

useOptimistic: мгновенная обратная связь в UI

React 19 стандартизирует оптимистичные обновления через useOptimistic:

const [optimisticName, setOptimisticName] = useOptimistic(currentName)

const submitAction = async (formData) => {
  const newName = formData.get("name")
  setOptimisticName(newName) // UI обновляется немедленно
  const result = await updateName(newName)
  onUpdateName(result)
}

React возвращает optimisticName к currentName, когда родительский компонент повторно рендерится с каноническим состоянием после завершения действия. Для неудачных действий убедитесь, что состояние вашего родителя остаётся неизменным, чтобы оптимистичное значение корректно откатилось.

Изменения в React Suspense и API use()

API use() позволяет читать промисы непосредственно во время рендеринга. В отличие от хуков, он работает внутри условий и циклов:

function Comments({ commentsPromise }) {
  const comments = use(commentsPromise)
  return comments.map(comment => <p key={comment.id}>{comment}</p>)
}

Когда промис находится в состоянии ожидания, use() приостанавливает компонент. Оберните его в границу Suspense, чтобы показать резервный UI.

Важное ограничение: use() не поддерживает промисы, созданные во время рендеринга. Промис должен быть кеширован или поступать из источника, совместимого с Suspense. Также нельзя использовать use() внутри блоков try-catch — вместо этого отклонения обрабатываются границами ошибок.

В серверных средах документация React рекомендует предпочитать async/await для семантики загрузки данных, используя use() в первую очередь для потребления уже управляемых промисов.

Что не изменилось

Паттерны конкурентного UI в React — базовое планирование, прерываемый рендеринг и система приоритетов — остаются фундаментом React 18. React 19 не вводит новых концепций конкурентности. Он предоставляет высокоуровневые API, которые используют существующие возможности конкурентности.

Изменение ментальной модели связано не с пониманием новой механики рендеринга. Речь идёт о признании того, что React теперь обрабатывает асинхронные состояния UI, которыми вы ранее управляли самостоятельно.

Практические последствия

Прекратите вручную создавать состояния ожидания. Используйте useTransition или useActionState вместо ручных переменных isPending.

Используйте Actions для мутаций. Любая асинхронная функция, обёрнутая в startTransition, становится Action с планированием, учитывающим переходы.

Используйте useOptimistic для отзывчивых UI. Паттерн теперь стандартизирован, а не реализуется специально для каждого случая.

Сочетайте use() с Suspense для загрузки данных. Но помните о требовании кеширования — это лучше всего работает с фреймворками или библиотеками, которые управляют стабильностью промисов.

Заключение

Асинхронный рендеринг в React 19 — это не новая парадигма. Это недостающий слой абстракции, который делает конкурентный React практичным для повседневных асинхронных рабочих процессов. Предоставляя встроенные API для состояний ожидания, обработки форм, оптимистичных обновлений и разрешения промисов, React 19 устраняет шаблонный код, который разработчики писали с момента введения конкурентного рендеринга в React 18. Фундамент остаётся прежним — изменилось то, насколько доступными и стандартизированными стали эти паттерны.

Часто задаваемые вопросы

Да. Actions работают в любом приложении на React 19. Хотя фреймворки вроде Next.js предоставляют дополнительную серверную интеграцию, основные API, такие как useTransition с асинхронными функциями, useActionState и useOptimistic, функционируют в клиентских React-приложениях без каких-либо зависимостей от фреймворков.

Не полностью. useOptimistic возвращается к исходному значению, когда родительский компонент повторно рендерится с неизменённым каноническим состоянием. Вы должны убедиться, что неудачные действия не обновляют это состояние, чтобы откат произошёл корректно.

Не обязательно. useTransition идеален для несрочных обновлений, где вы хотите, чтобы React сохранял текущий UI отзывчивым. Для критических индикаторов загрузки, которые должны блокировать взаимодействие, традиционные паттерны useState могут оставаться уместными.

Технически да, но это требует тщательного управления промисами. Промис, передаваемый в use(), должен быть стабильным между рендерами, то есть вы не можете создавать его во время рендеринга. Используйте слой кеширования, библиотеку для загрузки данных или загрузку данных на уровне фреймворка, чтобы обеспечить стабильность промисов.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay