{post.title}
...
React 19 на подходе. Команда React анонсировала предрелизную версию React 19 в апреле. Это крупное обновление принесет с собой ряд улучшений и новых паттернов, нацеленных на повышение производительности, удобство использования и опыта разработки.
Многие из этих возможностей были представлены в экспериментальном режиме в React 18, но в React 19 они станут стабильными. Давайте подготовимся к этому обновлению.
Серверные компоненты — одно из крупнейших изменений в React с момента его первого релиза 10 лет назад. Они служат фундаментом для новых функций React 19, улучшая:
В этой статье мы не будем углубляться в серверные компоненты и стратегии рендеринга. Однако, чтобы в полной мере оценить роль серверных компонентов, необходимо кратко рассмотреть, как развивался процесс рендеринга в React.
Изначально React использовал клиентский рендеринг (Client-Side Rendering — CSR), который отправлял пользователю минимальный HTML.
Прикрепленный скрипт включает в себя весь код приложения — React, сторонние зависимости/библиотеки и собственный код. По мере роста приложения размер этого «пакета» кода становился все больше, что замедляло начальную загрузку. Когда пользователь переходил на страницу, сначала он видел пустой экран, пока этот JS-файл загружался, разбирался браузером, и пока React загружал DOM-элементы в пустой div
.
Чтобы скрыть этот некрасивый «пустой» период, разработчики стали применять «скелетоны» — временные визуальные заглушки, которые отображались до загрузки и рендеринга реальных данных.
React усовершенствовался благодаря серверному рендерингу (Server-Side Rendering — SSR). Этот подход предполагает, что первоначальный рендеринг происходит на сервере, а не на клиенте, что позволяет отправлять пользователю HTML-код с готовым начальным интерфейсом, ускоряя его отображение. Однако даже в этом случае для показа реального содержимого страницы требуется дополнительная загрузка данных с сервера.
Фреймворки React расширили свои возможности, чтобы улучшить пользовательский опыт, внедрив такие концепции, как генерация статического контента (Static-Site Generation — SSG) и его инкрементная регенерация (Incremental Static Regeneration — ISR).
SSG предполагает предварительную сборку и кэширование динамических данных во время генерации сайта. ISR дает возможность обновлять этот кэш по мере необходимости, без полной перезагрузки страницы.
И, наконец, появились серверные компоненты React (React Server Components — RSC). Впервые в самом React появилась возможность загружать данные до рендеринга и отображения пользовательского интерфейса.
export default async function Page() {
const res = await fetch('https://api.example.com/products')
const products = res.json()
return (
<>
Products
{products.map((product) => (
{product.title}
{product.description}
))}
>
)
}
HTML, предоставляемый пользователю, полностью заполнен реальным содержимым при первом рендеринге. Нет необходимости в дополнительной загрузке данных или повторном рендеринге.
Появление серверных компонентов — это большой шаг вперед в повышении скорости и производительности приложений. Подробнее ознакомиться с RSC можно здесь.
Визуальные иллюстрации рендеринга вдохновлены работами Josh W. Comeau.
Директивы не являются особенностью React 19, но тесно связаны с этой версией фреймворка. Из-за введения RSC, у сборщиков появилась необходимость различать, где именно выполняется код компонентов и функций — на клиенте или на сервере. Для этого введены две новые директивы:
'use client'
— отмечает код, который работает только на клиенте. Поскольку серверные компоненты являются стандартными, 'use client'
добавляется в клиентские компоненты при использовании хуков для интерактивности и состояния.'use server'
— отмечает серверные функции, которые могут вызываться из клиентского кода. Не нужно добавлять 'use server
' к серверным компонентам, только к серверным операциям (подробнее об этом ниже). Если требуется, чтобы определенный код выполнялся только на сервере, можно использовать пакет server-only
.Подробнее о директивах можно узнать здесь.
React 19 вводит концепцию операций (actions). Эти функции заменяют обработчики событий и интегрируются с переходами (transitions) и параллельными (concurrent) возможностями React.
Операции могут использоваться как на клиенте, так и на сервере. Например, можно создать операцию, которая будет обрабатывать отправку формы вместо традиционного обработчика onSubmit
.
Вместо необходимости обрабатывать событие, в операцию напрямую передается объект FormData
:
import { useState } from 'react'
export default function TodoApp() {
const [items, setItems] = useState([{ text: 'My first todo' }])
async function formAction(formData) {
const newItem = formData.get('item')
// Отправляет POST-запрос на сервер для сохранения нового элемента
setItems((items) => [...items, { text: newItem }])
}
return (
<>
Todo List
{items.map((item, index) => (
- {item.text}
))}
>
)
}
Серверные операции (server actions) позволяют клиентским компонентам вызывать асинхронные функции, выполняемые на сервере. Это дает дополнительные преимущества, такие как возможность чтения файловой системы или прямое обращение к базе данных, устраняя необходимость создания специальных конечных точек API для пользовательского интерфейса.
Операции определяются с помощью директивы 'use server'
и интегрируются с клиентскими компонентами.
Чтобы использовать серверную операцию в клиентском компоненте, необходимо создать новый файл и импортировать его:
'use server'
export async function create() {
// Сохраняем данные в БД
}
'use client'
import { create } from './actions'
export default function TodoList() {
return (
<>
Todo List
>
)
}
Подробнее о серверных операциях можно узнать здесь.
Для того, чтобы дополнить концепцию операций, в React 19 представлены три новых хука, которые упрощают работу с состоянием, статусом и визуальной обратной связью. Эти хуки особенно полезны при работе с формами, но могут быть использованы и для других элементов, например, для кнопок.
Хук useActionState
упрощает работу с состоянием форм и их отправкой. Используя операции, он обрабатывает данные, вводимые в форму, валидацию и ошибки, избавляя от необходимости написания клиентской логики управления состоянием. Этот хук также предоставляет состояние pending
, которое можно использовать для отображения индикатора загрузки во время выполнения операции:
'use client'
import { useActionState } from 'react'
import { createUser } from './actions'
const initialState = {
message: '',
}
export function Signup() {
const [state, formAction, pending] = useActionState(createUser, initialState)
return (
)
}
Подробнее о useActionState
можно узнать здесь.
Хук useFormStatus
отслеживает статус последней отправки формы. Он должен вызываться из компонента, который находится в составе формы:
import { useFormStatus } from 'react-dom'
import action from './actions'
function Submit() {
const status = useFormStatus()
return
}
export default function App() {
return (
)
}
В то время как useActionState
имеет встроенное состояние pending
, хук useFormStatus
может быть особенно полезен в следующих ситуациях:
useFormStatus
возвращает информацию о статусе только родительской формыПодробнее о useFormStatus
можно узнать здесь.
Этот хук позволяет оптимистично обновлять пользовательский интерфейс до завершения серверной операции, вместо ожидания ответа. Когда асинхронная операция завершается, пользовательский интерфейс обновляется с окончательным состоянием, полученным с сервера.
Следующий пример демонстрирует, как можно добавить новое сообщение в тред, не дожидаясь завершения отправки сообщения на сервер:
'use client'
import { useOptimistic } from 'react'
import { send } from './actions'
export function Thread({ messages }) {
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [...state, { message: newMessage }],
)
const formAction = async (formData) => {
const message = formData.get('message')
addOptimisticMessage(message)
await send(message)
}
return (
{optimisticMessages.map((m, i) => (
{m.message}
))}
)
}
Подробнее о useOptimistic
можно узнать здесь.
Функция use
предназначена для работы с промисами и контекстом во время рендеринга. В отличие от других хуков React, use
может вызываться внутри циклов, условных операторов и ранних возвратов. Обработка ошибок и отображение загрузки выполняются ближайшим компонентом Suspense
.
Следующий пример демонстрирует, как отображать сообщение о загрузке во время выполнения промиса для получения данных корзины товаров:
import { use } from 'react'
function Cart({ cartPromise }) {
// Функция `use` приостанавливает выполнение компонента, пока промис не будет разрешен
const cart = use(cartPromise)
return cart.map((item) => {item.title}
)
}
function Page({ cartPromise }) {
return (
// Во время приостановки выполнения `Cart`, отображается этот `Suspense`
Loading...
Это позволяет объединить компоненты в группу, чтобы они рендерились только тогда, когда доступны данные для всех входящих в нее компонентов.
Подробнее о use
можно узнать здесь.
В React 19 добавлено несколько новых API-интерфейсов для повышения производительности приложений и улучшения пользовательского опыта. Эти API позволяют выполнять обычную и предварительную загрузку ресурсов (скрипты, таблицы стилей и шрифты):
prefetchDNS
осуществляет предварительную загрузку IP-адреса доменного имени DNS, с которым ожидается соединениеpreconnect
устанавливает соединение с сервером, от которого ожидается запрос ресурсов, даже если точные ресурсы неизвестныpreload
выполняет предварительную загрузку таблицы стилей, шрифта, изображения или внешнего скрипта, которые планируется использоватьpreloadModule
осуществляет предварительную загрузку модуля ESM, который планируется использоватьpreinit
выполняет предварительную загрузку и оценку (evaluation) внешнего скрипта или предварительную загрузку и вставку таблицы стилейpreinitModule
выполняет предварительную загрузку и оценку модуля ESMИспользование данных API-интерфейсов в React-коде приведет к соответствующей разметке HTML, где ссылки и скрипты будут упорядочены по приоритету загрузки, а не по порядку их использования:
// React code
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
preinit('https://.../path/to/some/script.js', { as: 'script' })
preload('https://.../path/to/some/font.woff', { as: 'font' })
preload('https://.../path/to/some/stylesheet.css', { as: 'style' })
prefetchDNS('https://...')
preconnect('https://...')
}
React-фреймворки часто берут на себя обработку загрузки ресурсов, в таких случаях нет необходимости самостоятельно вызывать эти API-интерфейсы.
Подробнее об API-интерфейсах предварительной загрузки ресурсов можно узнать здесь.
ref
как пропБольше нет необходимости использовать forwardRef
. React предоставит codemod для облегчения перехода на новый метод.
function CustomInput({ placeholder, ref }) {
return
}
//
ref
Внутри ref
можно возвращать функцию для очистки, которая вызывается при размонтировании компонента:
{
// Создание ref
return () => {
// Очистка ref
}
}}
/>
Context
как провайдерБольше нет необходимости в использовании
. Вместо этого можно использовать непосредственно
. React предоставит codemod для конвертации существующих провайдеров.
const ThemeContext = createContext('')
function App({ children }) {
return {children}
}
useDeferredValue
В хук useDeferredValue
была добавлена настройка initialValue
. При ее указании useDeferredValue()
будет использовать это значение для первоначального рендеринга, а затем запланирует повторный рендеринг в фоновом режиме, возвращая deferredValue
:
function Search({ deferredValue }) {
// При первоначальном рендеринге `value` равняется пустой строке ''.
// Затем планируется повторный рендеринг для обновления `value` значением `deferredValue`
const value = useDeferredValue(deferredValue, '')
return
}
В React 19 появилась встроенная возможность динамически формировать и отображать теги title
, link
и meta
, даже если они определены во вложенных компонентах. Больше нет необходимости в использовании сторонних решений для управления этими тегами.
function BlogPost({ post }) {
return (
{post.title}
{post.title}
...
)
}
В React 19 появилась возможность управлять порядком загрузки таблиц стилей с учетом их приоритета (precedence
). Такой подход позволяет более органично размещать таблицы стилей вместе с их компонентами, при этом React будет подгружать их только по мере необходимости.
Вот несколько ключевых моментов:
. Это гарантирует, что браузер не начнет отображение контента, пока таблица стилей не будет полностью загружена
на клиентской стороне до отображения контента, зависящего от этих стилей, с помощью Suspense
function ComponentOne() {
return (
...
)
}
function ComponentTwo() {
return (
...
{/* "three" будет вставлена между "one" и "two" */}
)
}
В React 19 появилась возможность рендерить асинхронные скрипты в любом компоненте. Это упрощает размещение скриптов рядом с соответствующими компонентами. React будет загружать их только при необходимости.
Вот несколько ключевых моментов:
и будут иметь более низкий приоритет по сравнению с более важными ресурсами, блокирующими отображение, такими как таблицы стилей, шрифты и предзагрузка изображенийfunction Component() {
return (
)
}
function App() {
return (
... // Скрипт не будет продублирован в DOM
)
}
Пользовательские элементы (custom elements) позволяют разработчикам определять собственные HTML-элементы согласно спецификации Web Components. В предыдущих версиях React использовать пользовательские элементы было сложно, потому что React обрабатывал неизвестные пропы как атрибуты, а не как свойства.
Теперь в React 19 реализована полноценная поддержка пользовательских элементов, и они успешно проходят все тесты на платформе Custom Elements Everywhere.
Удаление дублирующихся сообщений об ошибках повышает эффективность их обработки.
Ранее, React выдавал ошибку дважды: один раз для первоначальной ошибки, а затем еще раз после неудачной попытки автоматического восстановления, за которой следовала информация об ошибке.
В React 19 ошибка отображается только один раз.
В React 19 ошибки гидратации отображаются более оптимально — вместо нескольких ошибок, отображается только одно сообщение об ошибке несоответствия (mismatch error). При этом сообщения об ошибках содержат информацию о том, как можно их исправить.
Пример сообщения об ошибке гидратации в React 18.
Пример улучшенного сообщения об ошибке гидратации в React 19.
В React 19 также улучшена обработка ошибок гидратации при использовании сторонних скриптов и браузерных расширений. Ранее, элементы, добавленные сторонними скриптами или браузерными расширениями, вызывали ошибку несоответствия. В React 19 такие неожиданные (unexpected) теги в head
и body
будут просто игнорироваться.
Кроме того, React 19 вводит две новые настройки для корневого элемента приложения, помимо существующей onRecoverableError
. Эти настройки должны помочь разобраться с причинами ошибок:
onCaughtError
срабатывает, когда React перехватывает ошибку в предохранителеonUncaughtError
срабатывает, когда ошибка не перехватывается предохранителемonRecoverableError
срабатывает, когда ошибка возникает, но автоматически устраняетсяРелиз React 19 стал важным этапом развития этого фреймворка. Он привнес множество новых, мощных возможностей. Эти усовершенствования повысили производительность React и сделали его использование более комфортным как для разработчиков, так и для пользователей.
Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале ↩