Эта статья — перевод оригинальной статьи «React v19»

Также я веду телеграм канал «Frontend по‑флотски», где рассказываю про интересные вещи из мира разработки интерфейсов.

Вступление

React v19 теперь доступен на npm!

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

  • Что нового в React 19

  • Улучшения в React 19

  • Как обновить

Список изменений см. в руководстве по обновлению.

Что нового в React 19

Actions

Чаще всего в приложениях React требуется выполнить мутацию данных, а затем обновить состояние в ответ. Например, когда пользователь отправляет форму для изменения своего имени, вы выполняете API-запрос, а затем обрабатываете ответ. Раньше вам приходилось вручную обрабатывать отложенные состояния, ошибки, оптимистичные обновления и последовательные запросы.

Например, вы можете обрабатывать состояние ожидания и ошибки в useState:

// до Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, setIsPending] = useState(false);

  const handleSubmit = async () => {
    setIsPending(true);
    const error = await updateName(name);
    setIsPending(false);
    if (error) {
      setError(error);
      return;
    } 
    redirect("/path");
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

В React 19 мы добавляем поддержку использования async-функций в переходах для автоматической обработки отложенных состояний, ошибок, форм и оптимистичных обновлений.

Например, вы можете использовать useTransition для обработки состояния ожидания:

// Использование состояния ожидания из Actions
function UpdateName({}) {
  const [name, setName] = useState("");
  const [error, setError] = useState(null);
  const [isPending, startTransition] = useTransition();

  const handleSubmit = () => {
    startTransition(async () => {
      const error = await updateName(name);
      if (error) {
        setError(error);
        return;
      } 
      redirect("/path");
    })
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

Async transition немедленно установит состояние isPending в true, выполнит асинхронный запрос(ы) и переключит isPending в false после всех переходов. Это позволит вам сохранить текущий пользовательский интерфейс отзывчивым и интерактивным, пока данные меняются.

По традиции функции, использующие асинхронные переходы, называются "Actions".

Действия автоматически управляют отправкой данных за вас:

Состояние ожидания: Действия предоставляют состояние ожидания, которое начинается в начале запроса и автоматически сбрасывается, когда фиксируется окончательное обновление состояния.

Оптимистичные обновления: Действия поддерживают новый хук useOptimistic, поэтому вы можете показывать пользователям мгновенную обратную связь во время отправки запросов.

Обработка ошибок: Действия обеспечивают обработку ошибок, поэтому вы можете отображать границы ошибок при неудачном запросе и автоматически возвращать оптимистичные обновления к их первоначальному значению.

Формы: Элементы <form> теперь поддерживают передачу функций в пропсы action и formAction. Передача функций в пропс action по умолчанию использует Actions и автоматически сбрасывает форму после отправки.

Опираясь на Actions, React 19 представляет useOptimistic для управления оптимистичными обновлениями, а также новый хук React.useActionState для обработки общих случаев для Actions. В react-dom мы добавляем <form> Actions для автоматического управления формами и useFormStatus для поддержки общих случаев для Actions в формах.

В React 19 приведенный выше пример можно упростить до:

// Использование <form> Actions и useActionState
function ChangeName({ name, setName }) {
  const [error, submitAction, isPending] = useActionState(
    async (previousState, formData) => {
      const error = await updateName(formData.get("name"));
      if (error) {
        return error;
      }
      redirect("/path");
      return null;
    },
    null,
  );

  return (
    <form action={submitAction}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>Update</button>
      {error && <p>{error}</p>}
    </form>
  );
}

В следующем разделе мы рассмотрим каждую из новых функций Action в React 19.

Новый хук: useActionState

Чтобы упростить работу с часто встречающимися случаями для Actions, мы добавили новый хук под названием useActionState:

const [error, submitAction, isPending] = useActionState(
  async (previousState, newName) => {
    const error = await updateName(newName);
    if (error) {
      // Вы можете вернуть любой результат действия.
      // Здесь мы возвращаем только ошибку.
      return error;
    }

    // обрабатываем успех
    return null;
  },
  null,
);

useActionState принимает функцию ("Action") и возвращает обернутый Action для вызова. Это работает, потому что действия компонуются. Когда обернутый Action будет вызвано, useActionState вернет последний результат Action'а как data, а состояние ожидания Action'а- как pending.

React.useActionState ранее назывался ReactDOM.useFormState в релизах Canary, но мы переименовали его и отменили useFormState.

Для получения дополнительной информации см. документацию по UseActionState.

React DOM: <form> Actions 

Действия также интегрированы с новыми возможностями <form> в React 19 для react-dom. Мы добавили поддержку передачи функций в качестве пропсов action и formAction элементов <form>, <input> и <button> для автоматической отправки форм с помощью Actions:

<form action={actionFunction}>

При успешном выполнении Action'а <form> React автоматически сбрасывает форму для неконтролируемых компонентов. Если вам нужно сбросить её вручную, вы можете вызвать новый API React DOM requestFormReset.

React DOM: Новый хук: useFormStatus

В системах проектирования часто встречаются компоненты дизайна, которым требуется доступ к информации о <form>, в которой они находятся, без необходимости передачи пропса до самого компонента. Это можно сделать с помощью Context, но чтобы упростить эту задачу, мы добавили новый хук useFormStatus:

import {useFormStatus} from 'react-dom';

function DesignButton() {
  const {pending} = useFormStatus();
  return <button type="submit" disabled={pending} />
}

useFormStatus считывает статус родительской <form>, как если бы форма была провайдером Context.

Для получения дополнительной информации см. документацию react-dom по useFormStatus.

Новый хук: useOptimistic

Еще один распространенный паттерн пользовательского интерфейса при выполнении мутации данных - оптимистичное отображение конечного состояния во время выполнения async-запроса. В React 19 мы добавили новый хук useOptimistic, чтобы упростить эту задачу:

function ChangeName({currentName, onUpdateName}) {
  const [optimisticName, setOptimisticName] = useOptimistic(currentName);

  const submitAction = async formData => {
    const newName = formData.get("name");
    setOptimisticName(newName);
    const updatedName = await updateName(newName);
    onUpdateName(updatedName);
  };

  return (
    <form action={submitAction}>
      <p>Your name is: {optimisticName}</p>
      <p>
        <label>Change Name:</label>
        <input
          type="text"
          name="name"
          disabled={currentName !== optimisticName}
        />
      </p>
    </form>
  );
}

Хук useOptimistic немедленно отобразит optimisticName, пока выполняется запрос updateName. Когда обновление завершится или произойдет ошибка, React автоматически переключится обратно на значение currentName.

Для получения дополнительной информации см. документацию по useOptimistic.

Новый API: use

В React 19 мы представляем новый API для чтения ресурсов при рендеринге: use.

Например, вы можете прочитать обещание с помощью use, и React приостановится (Suspend) до тех пор, пока Promise не разрешится:

import {use} from 'react';

function Comments({commentsPromise}) {
  // `use` будет приостановлено до тех пор, пока обещание не разрешится.
  const comments = use(commentsPromise);
  return comments.map(comment => <p key={comment.id}>{comment}</p>);
}

function Page({commentsPromise}) {
  // Когда `use` приостанавливается в Comments,
  // будет показана этот Suspense.
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  )
}

Вы также можете читать контекст с use, что позволит вам читать контекст условно, например, после досрочного возвращения:

import {use} from 'react';
import ThemeContext from './ThemeContext'

function Heading({children}) {
  if (children == null) {
    return null;
  }
  
  // Это не будет работать с useContext
  // из-за раннего возврата.
  const theme = use(ThemeContext);
  return (
    <h1 style={{color: theme.color}}>
      {children}
    </h1>
  );
}

API use может быть вызван только в рендере, как и хуки. В отличие от хуков, use можно вызывать условно. В будущем мы планируем поддерживать больше способов потребления ресурсов в рендере с помощью use.

Для получения дополнительной информации смотрите документацию по use.

Новые статические API React DOM

Мы добавили два новых API в react-dom/static для генерации статических сайтов:

Эти новые API улучшают renderToString, ожидая загрузки данных для генерации статического HTML. Они предназначены для работы с потоковыми средами, такими как Node.js Streams и Web Streams. Например, в среде Web Stream вы можете предварительно сгенерировать дерево React в статический HTML с помощью prerender:

import { prerender } from 'react-dom/static';

async function handler(request) {
  const {prelude} = await prerender(<App />, {
    bootstrapScripts: ['/main.js']
  });
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  });
}

API Prerender будет ждать, пока загрузятся все данные, прежде чем вернуть статический HTML-поток. Потоки могут быть преобразованы в строки или отправлены вместе с потоковым ответом. Они не поддерживают потоковую передачу контента по мере его загрузки, что поддерживается существующими React DOM server rendering APIs.

Для получения дополнительной информации смотрите раздел React DOM Static APIs.

React Server Components 

Server Components 

Серверные компоненты - это новая опция, которая позволяет рендерить компоненты заранее, до объединения, в среде, отдельной от вашего клиентского приложения или сервера SSR. Эта отдельная среда является "сервером" в React Server Components. Серверные компоненты могут запускаться один раз во время сборки на вашем CI-сервере, или же они могут запускаться для каждого запроса с помощью веб-сервера.

React 19 включает в себя все функции React Server Components из канала Canary. Это означает, что библиотеки, поставляемые с серверными компонентами, теперь могут использовать React 19 как зависимость от других компонентов с условием экспорта react-server для использования во фреймворках, поддерживающих архитектуру Full-stack React.

Как создать поддержку серверных компонентов?
В то время как React Server Components в React 19 стабильны и не ломаются между основными версиями, базовые API, используемые для реализации бандлера или фреймворка React Server Components, не следуют semver и могут ломаться между основными версиями в React 19.x.

Для поддержки React Server Components в качестве бандлера или фреймворка мы рекомендуем привязаться к определенной версии React или использовать релиз Canary. Мы продолжим работу с бандлерами и фреймворками, чтобы стабилизировать API, используемые для реализации React Server Components в будущем.

Подробнее см. документацию по React Server Components.

Server Actions 

Действия сервера позволяют клиентским компонентам вызывать асинхронные функции, выполняемые на сервере.

Когда действие сервера определяется с помощью директивы "use server", ваш фреймворк автоматически создает ссылку на функцию сервера и передает ее клиентскому компоненту. Когда эта функция будет вызвана на клиенте, React отправит запрос на сервер для выполнения функции и вернет результат.

Директивы для серверных компонентов не существует.
Распространенное заблуждение заключается в том, что серверные компоненты обозначаются "use server", но директивы для серверных компонентов не существует. Директива "use server" используется для Server Actions.

Дополнительную информацию см. в документации по директивам.

Server Actions могут быть созданы в server components и переданы в качестве пропсов компонентам клиента, или же они могут быть импортированы и использованы в компонентах клиента.

Подробнее см. документацию по React Server Actions.

Улучшения в React 19

ref как проп 

Начиная с React 19, вы можете использовать ref в качестве пропса для компонентов функций:

function MyInput({placeholder, ref}) {
  return <input placeholder={placeholder} ref={ref} />
}

//...
<MyInput ref={ref} />

Новые компоненты функций больше не будут нуждаться в forwardRef, и мы опубликуем кодмод для автоматического обновления ваших компонентов для использования нового пропса ref. В будущих версиях мы откажемся от использования forwardRef и удалим его.

refs, передаваемые классам, не передаются как пропсы , поскольку они ссылаются на экземпляр компонента.

Диффы для ошибок гидратации

Мы также улучшили отчет об ошибках гидратации в react-dom. Например, вместо того, чтобы регистрировать несколько ошибок в DEV без какой-либо информации о несоответствии:

Теперь мы выводим в журнал одно сообщение с диффом несоответствия:

 <Context > как provider

В React 19 вы можете отображать <Context> как провайдер вместо <Context.Provider>:

const ThemeContext = createContext('');

function App({children}) {
  return (
    <ThemeContext value="dark">
      {children}
    </ThemeContext>
  );  
}

Новые провайдеры контекста могут использовать <Context>, и мы опубликуем кодмод для преобразования существующих провайдеров. В будущих версиях мы откажемся от <Context.Provider>.

Функции очистки для refs

Теперь мы поддерживаем возврат функции очистки из обратных вызовов ref:

<input
  ref={(ref) => {
    // ref создан

    // НОВОЕ: возврат функции очистки для сброса
    // ссылка, когда элемент удаляется из DOM.
    return () => {
      // очистка ref
    };
  }}
/>

Когда компонент размонтируется, React вызовет функцию очистки, возвращенную из обратного вызова ref. Это работает для ссылок на DOM, ссылок на компоненты классов и useImperativeHandle.

Ранее React вызывал функции ref с null при размонтировании компонента. Если ваша ref-функция возвращает функцию очистки, React теперь будет пропускать этот шаг.

В будущих версиях мы откажемся от вызова refs с null при размонтировании компонентов.

Благодаря введению функций очистки ref, возврат чего-либо другого из обратного вызова ref теперь будет отменяться TypeScript'ом. Исправление обычно заключается в том, чтобы перестать использовать неявные возвраты, например:

- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />

Оригинальный код возвращал экземпляр HTMLDivElement, и TypeScript не знал, должна ли это быть функция очистки или вы не хотите возвращать функцию очистки.

Вы можете модифицировать этот паттерн с помощью no-implicit-ref-callback-return.

Начальное значение useDeferredValue

Мы добавили опцию initialValue в useDeferredValue:

function Search({deferredValue}) {
  // При первоначальном отображении значение будет ''.
  // Затем планируется повторный рендеринг с отложенным значением (deferredValue).
  const value = useDeferredValue(deferredValue, '');
  
  return (
    <Results query={value} />
  );
}

Если initialValue указано, useDeferredValue вернет его в качестве значения для начального рендеринга компонента и запланирует повторный рендеринг в фоновом режиме с возвращенным deferredValue.

Подробнее см. в разделе useDeferredValue.

Поддержка для Document Metadata 

В HTML теги метаданных документа, такие как <title>, <link> и <meta>, предназначены для размещения в разделе <head> документа. В React компонент, который решает, какие метаданные подходят для приложения, может находиться очень далеко от места, где вы отображаете <head>, или React вообще не отображает <head>. Раньше эти элементы нужно было вставлять вручную в эффект или с помощью библиотек вроде react-helmet, и они требовали тщательной обработки при серверном рендеринге React-приложения.

В React 19 мы добавляем поддержку нативного рендеринга тегов метаданных документа в компонентах:

function BlogPost({post}) {
  return (
    <article>
      <h1>{post.title}</h1>
      <title>{post.title}</title>
      <meta name="author" content="Josh" />
      <link rel="author" href="https://twitter.com/joshcstory/" />
      <meta name="keywords" content={post.keywords} />
      <p>
        Eee equals em-see-squared...
      </p>
    </article>
  );
}

Когда React отобразит этот компонент, он увидит теги <title> , <link> и <meta> и автоматически поднимет их в раздел <head> документа. Поддерживая эти теги метаданных нативно, мы можем гарантировать, что они будут работать с приложениями, предназначенными только для клиентов, потоковыми SSR и серверными компонентами.

Возможно, вам все еще нужна библиотека метаданных
Для простых случаев использования может подойти рендеринг метаданных документа в виде тегов, но библиотеки могут предложить более мощные возможности, такие как переопределение общих метаданных с конкретными метаданными, основанными на текущем маршруте. Эти возможности облегчают работу фреймворков и библиотек, таких как react-helmet, поддерживая теги метаданных, а не заменяя их.

Более подробную информацию можно найти в документации по <title>, <link> и <meta>.

Поддержка таблиц стилей

Таблицы стилей, как внешние (<link rel=«stylesheet» href=«...»>), так и встроенные (<style>...</style>), требуют тщательного позиционирования в DOM из-за правил приоритета стилей. Создать возможности таблицы стилей, обеспечивающие совместимость внутри компонентов, очень сложно, поэтому пользователи часто либо загружают все свои стили вдали от компонентов, которые могут от них зависеть, либо используют библиотеку стилей, которая инкапсулирует эту сложность.

В React 19 мы решаем эту проблему и обеспечиваем еще более глубокую интеграцию в Concurrent Rendering на клиенте и Streaming Rendering на сервере со встроенной поддержкой таблиц стилей. Если вы укажете React приоритет вашей таблицы стилей, она будет управлять порядком вставки таблицы стилей в DOM и обеспечит загрузку таблицы стилей (если она внешняя) перед отображением контента, зависящего от этих стилевых правил.

function ComponentOne() {
  return (
    <Suspense fallback="loading...">
      <link rel="stylesheet" href="foo" precedence="default" />
      <link rel="stylesheet" href="bar" precedence="high" />
      <article class="foo-class bar-class">
        {...}
      </article>
    </Suspense>
  )
}

function ComponentTwo() {
  return (
    <div>
      <p>{...}</p>
      <link rel="stylesheet" href="baz" precedence="default" />  <-- will be inserted between foo & bar
    </div>
  )
}

Во время рендеринга на стороне сервера React включит таблицу стилей в <head>, что гарантирует, что браузер не будет рисовать до ее загрузки. Если таблица стилей будет обнаружена поздно, после того как мы уже начали потоковую передачу, React убедится, что таблица стилей вставлена в <head> на клиенте, прежде чем открыть содержимое границы Suspense, которая зависит от этой таблицы стилей.

Во время рендеринга на стороне клиента React будет ждать загрузки новых рендерных таблиц стилей перед фиксацией рендеринга. Если вы рендерите этот компонент из нескольких мест в вашем приложении, React включит таблицу стилей в документ только один раз:

function App() {
  return <>
    <ComponentOne />
    ...
    <ComponentOne /> // не приведет к дублированию ссылки на таблицу стилей в DOM
  </>
}

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

Библиотеки стилей и интеграции стилей с бандлерами также могут использовать эту новую возможность, так что даже если вы не занимаетесь непосредственным рендерингом собственных таблиц стилей, вы все равно сможете извлечь выгоду, поскольку ваши инструменты будут обновлены для использования этой функции.

Для получения более подробной информации читайте документацию по link и style.

Поддержка async scripts 

В HTML обычные скрипты (<script src=«...»>) и отложенные скрипты (<script defer=«» src=«...»>) загружаются в порядке следования документов, что делает сложным рендеринг этих типов скриптов в глубине дерева компонентов. Асинхронные скрипты (<script async=«» src=«...»>), однако, загружаются в произвольном порядке.

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

function MyComponent() {
  return (
    <div>
      <script async={true} src="..." />
      Hello World
    </div>
  )
}

function App() {
  <html>
    <body>
      <MyComponent>
      ...
      <MyComponent> // не приведет к дублированию скрипта в DOM
    </body>
  </html>
}

Во всех средах рендеринга async-скрипты будут дедуплицированы, так что React загрузит и выполнит скрипт только один раз, даже если он будет отрисован несколькими различными компонентами.

При рендеринге на стороне сервера async-скрипты будут включаться в <head> и приоритетнее более важных ресурсов, блокирующих закраску, таких как таблицы стилей, шрифты и предварительная загрузка изображений.

Для получения более подробной информации читайте документацию по script.

Поддержка предварительной загрузки ресурсов

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

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

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
  preinit('https://.../path/to/some/script.js', {as: 'script' }) // загружает и выполняет этот скрипт с нетерпением
  preload('https://.../path/to/font.woff', { as: 'font' }) // предварительно загружает этот шрифт
  preload('https://.../path/to/stylesheet.css', { as: 'style' }) // предварительно загружает эту таблицу стилей
  prefetchDNS('https://...') // когда вы можете ничего не запрашивать у этого хоста.
  preconnect('https://...') // когда вы хотите что-то попросить, но не уверены, что именно.
}
<!-- Вышеописанное приведет к следующему DOM/HTML -->
<html>
  <head>
    <!-- ссылки/скрипты приоритетны по их полезности для ранней загрузки, а не по порядку вызова -->
    <link rel="prefetch-dns" href="https://...">
    <link rel="preconnect" href="https://...">
    <link rel="preload" as="font" href="https://.../path/to/font.woff">
    <link rel="preload" as="style" href="https://.../path/to/stylesheet.css">
    <script async="" src="https://.../path/to/some/script.js"></script>
  </head>
  <body>
    ...
  </body>
</html>

Эти API можно использовать для оптимизации начальной загрузки страницы, перенося обнаружение дополнительных ресурсов, таких как шрифты, из загрузки таблицы стилей. Они также могут ускорить обновление клиента за счет предварительной выборки списка ресурсов, используемых при предполагаемой навигации, и последующей предварительной загрузки этих ресурсов при нажатии или даже при наведении курсора.

Более подробную информацию можно найти в разделе API Resource Preloading.

Совместимость со сторонними скриптами и расширениями

Мы улучшили гидратацию для учета сторонних скриптов и расширений браузера.

При гидратации, если элемент, отображаемый на клиенте, не совпадает с элементом, найденным в HTML на сервере, React заставит клиента пересмотреть содержимое, чтобы исправить его. Ранее, если элемент был вставлен сторонними скриптами или расширениями браузера, это приводило к ошибке несоответствия и повторному рендерингу клиента.

В React 19 неожиданные теги в head и body будут пропускаться, что позволит избежать ошибок несоответствия. Если React необходимо перерисовать весь документ из-за несоответствия, не связанного с гидрацией, он оставит на месте таблицы стилей, вставленные сторонними скриптами и расширениями браузера.

Улучшенные отчеты об ошибках

Мы улучшили обработку ошибок в React 19, чтобы убрать дублирование и предоставить опции для обработки пойманных и не пойманных ошибок. Например, при ошибке рендеринга, пойманной Error Boundary ошибки, ранее React выбрасывал ошибку дважды (один раз для исходной ошибки, а затем еще раз после того, как не удалось автоматически восстановиться), а затем вызывал console.error с информацией о том, где произошла ошибка.

Это приводило к трем ошибкам на каждую пойманную ошибку:

В React 19 мы регистрируем одну ошибку со всей информацией о ней:

Кроме того, мы добавили две новые опции root в дополнение к onRecoverableError:

  • onCaughtError: вызывается, когда React ловит ошибку в Error Boundary .

  • onUncaughtError:вызывается, когда возникает ошибка, не пойманная Error Boundary .

  • onRecoverableError: вызывается при возникновении ошибки и автоматически восстанавливается.

Дополнительную информацию и примеры можно найти в документации по createRoot и hydrateRoot.

Поддержка пользовательских элементов

В React 19 добавлена полная поддержка пользовательских элементов и пройдены все тесты на Custom Elements Everywhere.

В прошлых версиях использование пользовательских элементов в React было затруднено, поскольку React рассматривал нераспознанные пропсы как атрибуты, а не свойства. В React 19 мы добавили поддержку свойств, которая работает на клиенте и во время SSR со следующей стратегией:

  • Рендеринг на стороне сервера: пропсы, переданные в пользовательский элемент, будут отображаться как атрибуты, если их тип является примитивным значением, таким как string, number, или значение равно true. Пропсы с непримитивными типами, такими как object, symbol, function, или значением false, будут опущены.

  • Рендеринг на стороне клиента: пропсы, которые соответствуют свойству экземпляра пользовательского элемента, будут назначены как свойства, в противном случае они будут назначены как атрибуты.

Спасибо Джоуи Архару за разработку и реализацию поддержки пользовательских элементов в React.

Как обновить

Пошаговые инструкции и полный список важных и значимых изменений см. в руководстве по обновлению React 19.

Комментарии (15)


  1. markelov69
    10.12.2024 13:01

    Когда используешь React в связке с MobX, то нет разницы, React v16 или v17 или v18 или v19, в нём все необходимое и так есть уже давным давно. Всё на что способен реакт, это рендерить DOM с помощью удобств JSX'a. Для всего остального есть MobX и он справляется на 3 головы лучше ректа.


    1. V_asily
      10.12.2024 13:01

      постойте ка, но ведь в статье нет ни слова про mobx ? Это такой способ привлечь внимание?


      1. DmitryKazakov8
        10.12.2024 13:01

        Это повод сказать, что Реакт очень медленно развивается, и этот релиз - в основном работа над ошибками, а не большой шаг вперед. Есть библиотеки как mobx, которые уже давно продвинули его на несколько шагов вперед, сделав удобный механизм работы со стейтом, и оставив Реакт только для рендера компонентов.

        Я думаю, что именно на этом Реакт и должен концентрироваться, а не перерастать в большую неэффективную систему, пробуя на аудитории новые подходы в виде хуков (функции в функции с сайд-эффектами и невидимым кешем) со строгими правилами (или не очень строгими, а очень странными, как в случае с новым use) или серверные компоненты.


        1. Alexandroppolus
          10.12.2024 13:01

          библиотеки как mobx, которые уже давно продвинули его на несколько шагов вперед

          Жаль, авторы реакта держатся в стороне от этой темы, игнорируя подход на проксях. Со встроенным механизмом типа мобикса реакт был бы превосходным. Понятно, что использовать-то можно в связке, но увы, это не стало общепринятым стандартом..


          1. DmitryKazakov8
            10.12.2024 13:01

            Мутабельность можно сделать и с помощью других паттернов, прокси вовсе не обязательны. Главное - интерфейс, что при мутации значения происходит ререндер. Что не нужно делать ни useReducer, ни setValue. И жесткие ограничения на интеграцию с другими системами реактивности Реакт тоже не красят - намерение плотно привязать на его экосистему и его подходы как раз и мешают более эффективным решениям "стать общепринятым стандартом". Как бы сообщество ни хотело исправить это - получаем только новую пачку хуков, решающих проблемы, которых и не должно было бы быть.


  1. undersunich
    10.12.2024 13:01

    Наблюдаю за Реактом годов так 5. Скажити- "входить" в Реакт уже можно или уже поздно ?


    1. CaptGg
      10.12.2024 13:01

      Если установить в браузер расширение React Developer Tools, иконка которого становится цветной на страницах использующих React, то складывается ощущение что он используется практически везде в крупных проектах. А значит "войти" стоит определенно и все еще не поздно.

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


    1. slbeat
      10.12.2024 13:01

      Есть еще компании для которых реакт это нано технологии модные и бессмысленные )


    1. themen2
      10.12.2024 13:01

      Лучше в Ангуляр, тут и места всем хватит и теплее


  1. imouseR
    10.12.2024 13:01

    Нельзя отказать команде React в грамотной стратегии, упорстве в намеченных целях и как следствие стремительном развитии экосистемы. Концентрация ресурсов в правильных руках, в данном случае, приносит ощутимую пользу сообществу. Поздравляю всех причастных!


  1. LyuMih
    10.12.2024 13:01

    Что по React Compiler?

    Единственная функция, которую по-настоящему ждал от 19 реакта



  1. Ares_ekb
    10.12.2024 13:01

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

    Например, фраза "В React 19 мы добавляем поддержку использования async-функций..." по-русски звучит проще "В React 19 добавлена поддержка async-функций...". В русском языке пассивный залог позволяет избавиться от лишней информации. В этой статье чуть ли не в каждом предложении "мы добавили", "мы улучшили" и т.д. И так понятно, что это сделали вы, зачем это постоянно повторять? С другой стороны, верно и обратное, когда переводишь с русского на английский, то нужно избавляться от пассивного залога, он усложняет понимание английского текста.

    Или фраза "Для получения дополнительной информации смотрите документацию по use" проще звучит "Дополнительная информация доступна в документации по use".

    "... чтобы упростить эту задачу, мы добавили новый хук useFormStatus" - проще звучит "... для упрощения этой задачи добавлен новый хук useFormStatus".

    "В прошлых версиях использование пользовательских элементов в React было затруднено, поскольку React рассматривал нераспознанные пропсы как атрибуты, а не свойства" - проще "В прошлых версиях React было сложно применять пользовательские элементы, т.к. нераспознанные пропсы считались атрибутами". Тоже звучит коряво, но, на мой взгляд, мозг запинается немного меньше.

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

    А потом люди начинают писать код и при его чтении у меня снова возникает ощущение, что они его получали от инопланетян из параллельной вселенной. На столько замысловато он иногда сформулирован. Вот, люди начитаются переводов, а потом и код пишут так же, например, строят лестницы в ад из Java-стримов:

    var sourceModelVersion = modelComponentsProperties.getModelVersionRepository()
            .findById(UUID.fromString(sourceObjectId))
            .orElseGet(() -> modelComponentsProperties.getModelElementRepository()
                    .findById(UUID.fromString(sourceObjectId))
                    .filter(ModelEntity.class::isInstance)
                    .map(ModelEntity.class::cast)
                    .map(ModelEntity::getModelVersion)
                    .orElseGet(() -> modelComponentsProperties.getReviewNoteRepository()
                            .findById(UUID.fromString(sourceObjectId))
                            .map(r -> findModelVersion(r.getModelElement().getId()))
                            .orElseGet(() -> modelComponentsProperties.getFigureRepository()
                                    .findById(UUID.fromString(sourceObjectId))
                                    .map(f -> findModelVersion(f.getModelElement().getId()))
                                    .orElse(null))));

    Если напрячь мозг, то конечно можно понять что имелось в виду. Но в ИТ и так достаточно вещей, над которыми можно напрягать мозг. Зачем создавать сложности искусственно? Текст должен читаться легко.

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


    1. AcckiyGerman
      10.12.2024 13:01

      Пха, большинство технологий стремится к усложнению, наверняка есть какой-то эмпирический закон на эту тему. Ведь только гениальное просто, а где гениев-то на всех набрать?


      1. Ares_ekb
        10.12.2024 13:01

        Я периодически с этим сталкиваюсь когда люди боятся трогать существующий код, пишут поверх всё больше и больше каких-то костылей. Даже строил графики по коммитам в Git, на которых видно, что подавляющее большинство людей только добавляют код и почти ничего не удаляют. А один-два человека пытаются бороться с этим хаосом и удаляют тысячи строк, при этом умудряются добавить новую функциональность или починить баги

        Достаточно сложно вовлечь всех разработчиков в этот процесс уничтожения кода, но в принципе это возможно. На самом деле ИТ отличается от науки или искусства тем, что все задачи здесь вполне формализуемые, декомопзируемые. И если в какой-то момент разработка просто останавливается из-за сложности, то это вполне решаемо