С вами Дима, старший фронтенд разработчик в Surf, и сегодня мы разберём React 19 — новую версию одной из самых популярных библиотек для создания пользовательских интерфейсов.
Команда React подготовила много интересного — обновление обещает упростить разработку, повысить производительность и открыть новые горизонты для создания современных приложений.
Так что вперёд, к подробному разбору основных фичей, оценке их плюсов и минусов и исследованию будущего React.
Что нового в React 19
React 19 — это не простой апгрейд, это полноценный шаг вперёд. В обновлении появились инструменты для автоматической оптимизации, новые подходы к серверному рендерингу и улучшения для повседневной работы.
1. React Compiler: турбоускоритель кода
React Compiler — это, пожалуй, самая громкая новинка версии. Представьте инструмент, который берёт React-код и автоматически превращает его в более быстрый JavaScript — а ещё избавляет от рутинной оптимизации. Компилятор может ускорить рендеринг до двух раз, устраняя необходимость вручную использовать useMemo, useCallback или оборачивать компоненты в memo.
Как это работает
Компилятор анализирует код на этапе сборки и определяет, какие части компонентов от каких данных зависят.
Затем он автоматически добавляет оптимизации: если данные не изменились, React не будет перерисовывать компонент. Это похоже на подход, который используют Svelte или Solid.js, где оптимизация происходит на этапе компиляции, но в React это сделано более гибко — компилятор пока опционален и не ломает существующий код.
Пример кода
Допустим, есть компонент, который выполняет сложные вычисления для рендера списка. В React 18 нам пришлось бы оптимизировать его вручную:
// React 18 import { memo, useMemo } from 'react'; const List = memo(({ items }) => { const computedItems = useMemo(() => { return items.map((item) => ({ ...item, value: expensiveComputation(item), })); }, [items]); return ( <ul> {computedItems.map((item) => ( <li key={item.id}>{item.value}</li> ))} </ul> ); }); function expensiveComputation(item) { // Имитация сложных вычислений return item.name.toUpperCase() + Math.random(); }
В React 19 с компилятором тот же код можно написать проще:
// React 19 function List({ items }) { const computedItems = items.map((item) => ({ ...item, value: expensiveComputation(item), })); return ( <ul> {computedItems.map((item) => ( <li key={item.id}>{item.value}</li> ))} </ul> ); } function expensiveComputation(item) { return item.name.toUpperCase() + Math.random(); }
Компилятор сам поймёт, что нужно кешировать, и предотвратит лишние рендеры. Это полезно в больших проектах, где ручная оптимизация занимает много времени.
Статус
К марту 2025 года React Compiler остаётся экспериментальной фичей, при этом он активно тестируется. Например, команда Instagram* использует его в продакшене, сообщая о заметном приросте производительности. В будущем компилятор станет частью стандартного рабочего процесса React, а его код откроют для сообщества.
Плюсы
-
Автоматическая оптимизация без лишнего кода.
-
Ускорение работы приложений даже без глубоких знаний о мемоизации.
-
Меньше багов из-за упущенных зависимостей в useMemo или useCallback.
Минусы
-
Экспериментальный статус требует тщательного тестирования перед использованием в продакшене.
-
Возможны конфликты с необычными паттернами кода, которые компилятор пока не поддерживает.
-
Требуется время на интеграцию в существующие проекты.
2. Server Components: сила сервера в ваших руках
Серверные компоненты (Server Components) — это новый подход к рендерингу, который позволяет частично перенести логику на сервер. Теперь можно пометить компонент директивой «use server», и он будет исполняться только на сервере, отправляя клиенту готовый HTML. Это ускорит загрузку страниц, улучшит SEO и снизит нагрузку на клиент.
Как это работает
Серверные компоненты рендерятся на сервере, а после их результат передаётся клиенту. При этом они могут взаимодействовать с клиентскими компонентами (помеченными как «use client»), создавая гибридную архитектуру. Например, тяжёлые операции вроде запросов к базе данных можно выполнять на сервере, а интерактивные элементы — оставить на клиенте.
Пример серверного компонента
// server-component.js 'use server'; export async function fetchUserData() { const res = await fetch('https://api.example.com/user'); return res.json(); } // page.js import { fetchUserData } from './server-component'; export default async function UserProfile() { const user = await fetchUserData(); return ( <div> <h1>{user.name}</h1> <p>{user.bio}</p> </div> ); }
Здесь fetchUserData выполняется на сервере, а клиент получает готовый HTML с данными пользователя. Если добавить клиентский компонент, например, кнопку, React сам разберётся, как соединить серверную и клиентскую части.
Реальный кейс
Допустим, мы строим блог. Серверные компоненты могут рендерить посты из базы данных, а клиентский компонент — форму комментариев. Это снижает объём JavaScript, отправляемого клиенту, и ускоряет первую загрузку.
Плюсы
-
Ускорение первой загрузка страницы благодаря серверному рендерингу.
-
Улучшение SEO — поисковики сразу видят контент.
-
Возможность выполнять ресурсоёмкие задачи — тот же парсинг данных — на сервере.
Минусы
-
Нужна поддержка фреймворка (например, Next.js), потому что React сам по себе не управляет сервером.
-
Не все компоненты можно сделать серверными — те, что используют window или другие браузерные API, останутся клиентскими.
-
Усложняет архитектуру, если мы не привыкли к разделению сервер-клиент.
3. Actions: прощайте сложности с формами
Actions — герой работы с формами и асинхронными мутациями в React 19. Это новый инструмент для управления отправкой данных, который автоматически обрабатывает состояния загрузки, ошибки и даже оптимистичные обновления интерфейса.
Как это работает
Вы определяете функцию Action и передаёте её в пропс action тега <form>. React берёт на себя управление состоянием: показывает, что форма отправляется, обрабатывает ошибки и позволяет обновлять UI до завершения операции.
Пример
import { useTransition } from 'react'; function CommentForm() { const [isPending, startTransition] = useTransition(); async function handleSubmit(formData) { startTransition(async () => { const response = await fetch('/api/comments', { method: 'POST', body: formData, }); if (!response.ok) throw new Error('Ошибка отправки'); }); } return ( <form action={handleSubmit}> <input name="comment" placeholder="Ваш комментарий" /> <button type="submit" disabled={isPending}> {isPending ? 'Отправка...' : 'Отправить'} </button> </form> ); }
Здесь useTransition помогает React управлять асинхронной операцией, а isPending показывает состояние загрузки. Можно добавить обработку ошибок или оптимистичное обновление с помощью хука useOptimistic.
Расширенный пример с ошибками
import { useTransition, useState } from 'react'; function CommentForm() { const [isPending, startTransition] = useTransition(); const [error, setError] = useState(null); async function handleSubmit(formData) { startTransition(async () => { try { const response = await fetch('/api/comments', { method: 'POST', body: formData, }); if (!response.ok) throw new Error('Ошибка сервера'); } catch (err) { setError(err.message); } }); } return ( <form action={handleSubmit}> <input name="comment" placeholder="Ваш комментарий" /> <button type="submit" disabled={isPending}> {isPending ? 'Отправка...' : 'Отправить'} </button> {error && <p style={{ color: 'red' }}>{error}</p>} </form> ); }
Плюсы
-
Упрощает работу с формами, избавляет от лишнего состояния.
-
Встроенная поддержка загрузки и ошибок.
-
Легко интегрируется с оптимистичными обновлениями через useOptimistic.
Минусы
-
Требует переосмысления привычных подходов к формам (например, если вы использовали onSubmit).
-
Может быть сложным для новичков, особенно при комбинировании с другими хуками.
-
Пока не все кейсы покрыты из коробки — например, сложные валидации не тронуты.
4. Новый API use: упрощаем работу с ресурсами
API use — это способ работать с асинхронными ресурсами (промисами, контекстом) прямо в рендере. В отличие от хуков, его можно вызывать условно, что даёт больше свободы.
Как это работает?
use принимает ресурс, например, промис, и возвращает его значение, когда оно готово. React приостанавливает рендеринг компонента, пока данные не загрузятся, и использует Suspense для показа fallback.
Пример
import { Suspense } from 'react'; function UserData({ userPromise }) { const user = use(userPromise); return <h1>{user.name}</h1>; } function App() { const promise = fetch('/api/user').then((res) => res.json()); return ( <Suspense fallback={<p>Загрузка...</p>}> <UserData userPromise={promise} /> </Suspense> ); }
Если промис ещё не разрешён, пользователь увидит «Загрузка…», а затем — имя пользователя.
Условный вызов
function Component({ condition, promise }) { if (condition) { const data = use(promise); return <div>{data}</div>; } return <div>Ничего нет</div>; }
Отметим, что это невозможно сделать с обычными хуками вроде useEffect, где вызовы должны быть строго на верхнем уровне.
Плюсы
-
Упрощает работу с асинхронными данными без лишних хуков.
-
Поддержка условных вызовов делает код более гибким.
-
Идеально сочетается с Suspense и серверными компонентами.
Минусы
-
Неочевидное поведение для тех, кто привык к строгим правилам хуков.
-
Требует осторожности, чтобы не вызвать бесконечные циклы рендеринга.
-
Ограниченная документация на ранних стадиях.
5. Улучшения в работе с рефами и хуками
React 19 упрощает работу с рефами и добавляет новые хуки для улучшения UX. Теперь рефы для функциональных компонентов передаются как обычные пропсы, и не нужно использовать forwardRef. Но главные звёзды этого обновления — новые хуки useFormStatus и useOptimistic. Они решают ряд задач разработчиков и делают взаимодействие с пользователем более плавным.
Пример рефов
// React 18 import { forwardRef } from 'react'; const MyInput = forwardRef((props, ref) => <input ref={ref} {...props} />); // React 19 function MyInput(props) { return <input {...props} />; } // Использование function Parent() { const inputRef = useRef(null); return <MyInput ref={inputRef} />; }
Новые хуки
useFormStatus
Этот хук даёт информацию о состоянии формы, в которой он используется. Он полезен, когда мы работаем с Actions и хотим отслеживать, отправляется ли форма в данный момент, или получать дополнительные данные о её статусе.
Хук возвращает объект с полями pending (булево значение, показывающее, выполняется ли отправка), data (объект FormData с данными формы) и method (метод формы, например, «POST» или «GET»).
Важно: useFormStatus работает только внутри компонента, который рендерится в контексте формы с пропсом action.
Пример с useFormStatus
import { useFormStatus } from 'react-dom'; function SubmitButton() { const { pending, data } = useFormStatus(); // Можно использовать data для дополнительных проверок const comment = data?.get('comment') || ''; return ( <button type="submit" disabled={pending}> {pending ? `Отправка "${comment}"...` : 'Отправить комментарий'} </button> ); } function CommentForm() { async function handleSubmit(formData) { await fetch('/api/comments', { method: 'POST', body: formData, }); } return ( <form action={handleSubmit}> <input name="comment" placeholder="Ваш комментарий" /> <SubmitButton /> </form> ); }
Кнопка «Отправить» становится неактивной во время отправки, а текст кнопки динамически обновляется, показывая, что именно отправляется. Это удобно для улучшения UX, особенно в формах с длительными операциями, например, загрузкой файлов.
Хук избавляет нас от необходимости вручную передавать состояние загрузки через пропсы или контекст — React сам отслеживает статус формы.
Когда использовать
-
Если нужно показать пользователю, что форма обрабатывается.
-
Когда требуется доступ к данным формы внутри дочерних компонентов без их явной передачи.
-
Для создания динамических интерфейсов, зависящих от статуса отправки.
Плюсы
-
Упрощает отслеживание состояния формы без лишнего кода.
-
Доступ к data позволяет создавать динамичные элементы UI, зависящие от введённых данных.
-
Улучшает UX за счёт встроенной обработки загрузки.
Минусы
-
Работает только внутри формы с action, что ограничивает его гибкость.
-
Не решает задачи сложной валидации или обработки ошибок.
-
Может быть избыточным для простых форм без асинхронных операций.
useOptimistic
Этот хук позволяет реализовать так называемое оптимистичное обновление UI. Что это значит? Оптимистичное обновление — это подход, при котором мы сразу показываем пользователю результат операции — добавление комментария или лайк — не дожидаясь ответа от сервера. Если операция завершится успешно, UI будет в актуальном состоянии. Если произойдёт ошибка, мы откатим изменения. Такой подход делает интерфейс более отзывчивым и улучшает восприятие скорости приложения.
Хук useOptimistic принимает текущее состояние и коллбэк для его «оптимистичного» обновления, возвращая массив с оптимистичным состоянием и функцией для его изменения.
Пример использования useOptimistic
import { useOptimistic } from 'react'; function CommentList({ comments, addComment }) { const [optimisticComments, addOptimisticComment] = useOptimistic(comments, (currentComments, newComment) => [ ...currentComments, { id: Date.now(), text: newComment, isPending: true }, ]); async function handleAddComment(text) { addOptimisticComment(text); // Сразу показываем комментарий try { await addComment(text); // Отправляем на сервер } catch (error) { console.error('Ошибка добавления комментария:', error); // Здесь можно откатить изменения, если нужно } } return ( <> <button onClick={() => handleAddComment('Новый комментарий')}> Добавить комментарий </button> <ul> {optimisticComments.map((comment) => ( <li key={comment.id} style={{ opacity: comment.isPending ? 0.5 : 1}}> {comment.text} {comment.isPending && '(ожидание)'} </li> ))} </ul> </> ); } // Пример функции addComment async function addComment(text) { await fetch('/api/comments', { method: 'POST', body: JSON.stringify({ text }), }); }
Новый комментарий появляется в списке сразу после клика, с полупрозрачностью и пометкой «(ожидание)», пока сервер не подтвердит операцию. Если запрос завершится успешно, UI уже готов. Если нет — можно добавить логику отката, например, удалить комментарий из списка.
Когда использовать
-
Для операций, где важна моментальная обратная связь — добавление лайков, комментариев, удаление записей.
-
Когда задержка сервера заметна, но мы хотим создать иллюзию мгновенной реакции.
-
В сочетании с Actions для форм с мутациями данных.
Плюсы
-
Делает интерфейс более отзывчивым, улучшая восприятие скорости.
-
Упрощает реализацию оптимистичных обновлений без сложной логики.
-
Гибкость в управлении временным состоянием через коллбэк.
Минусы
-
Требует ручной обработки ошибок и откатов при неудачных операциях.
-
Может запутать пользователя, если сервер часто возвращает ошибки.
-
Добавляет сложность в UX-дизайн для согласования оптимистичного и реального состояния.
Общие плюсы
-
Меньше бойлерплейта с рефами.
-
Новые хуки упрощают работу с формами и UX.
-
Код становится чище и читаемее.
Общие минусы
-
Придётся обновить существующие компоненты, использующие forwardRef.
-
Новые хуки пока не интуитивны для всех разработчиков.
Чего ждём от React
React движется к философии «пиши меньше, делай больше». Ждём:
-
полную интеграцию компилятора;
-
улучшение серверных возможностей и кеширования;
-
лучший DX с новыми инструментами и сообщениями об ошибках;
-
более глубокую совместимость с веб-компонентами.
Стоит ли переходить
В новых проектах — однозначно да. В старых — лучше потестировать в песочнице и оценить выгоды. Хотя React 19 обещает плавный переход с помощью codemods и документации.
Что в итоге
React 19 — это эволюция, которая делает разработку удобнее и быстрее. Да, есть нюансы, но преимущества перевешивают. Пробовали ли вы новые фичи? Делитесь опытом в комментариях! Пишите крутой код и до встречи!
Больше полезного про веб-разработку — в Telegram-канале Surf Web Team
Кейсы, лучшие практики, новости и вакансии в команду Web Surf в одном месте. Присоединяйтесь!
*Компания Meta Platforms Inc. (Facebook и Instagram) признана экстремистской организацией, ее деятельность на территории России запрещена.
ссылка на оригинал статьи https://habr.com/ru/articles/897200/
Добавить комментарий