Как стать автором
Обновить
81.45
Surf
Создаём веб- и мобильные приложения

React 19: что нового, что полезного, и куда мы движемся

Уровень сложностиСредний
Время на прочтение10 мин
Количество просмотров4.4K

С вами Дима, старший фронтенд разработчик в 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) признана экстремистской организацией, ее деятельность на территории России запрещена.

Теги:
Хабы:
+3
Комментарии9

Публикации

Информация

Сайт
surf.ru
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия

Истории