Наконец-то ожидание закончилось!

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

Знание React требуется для понимания обновлений React 18. Если вы новичок, просто следуйте этой статье для дорожной карты в React.

Посмотрим, что это принесет!

Новый корневой API

React 18 имеет два корневых API: старый корневой API и новый.

Что такое корневой API?

Согласно официальным документам React, «корень» — это указатель на структуру данных верхнего уровня React для отслеживания дерева для рендеринга.

Устаревший корневой API

Существующий корневой API называется ReactDOM.render. В настоящее время корень прикреплен к элементу DOM, и доступ к нему можно получить через узел DOM.

Он будет работать точно так же, как в React 17. Однако консоль выдаст предупреждение, указывающее на его устаревание и переход на последнюю версию API.

import  React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import App from './App
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

Новый корневой API

Он поставляется с ReactDOM.createRoot, включает новый параллельный рендеринги открывает доступ ко всем улучшениям, представленным в Реагировать 18.

import * as ReactDOMClient from 'react-dom/client';
import App from 'App';

const container = document.getElementById('app');

// Create a root.
const root = ReactDOMClient.createRoot(container);

// Initial render: Render an element to the root.
root.render(<App tab="home" />);

// During an update, there's no need to pass the container again.
root.render(<App tab="profile" />);

Функция createRoot сначала создаст корень, а затем вызовет метод render для него.

Как справиться с гидратацией с помощью нового Root API?

Гидратация теперь обрабатывается через hydraRoot API.

import * as ReactDOMClient from 'react-dom/client';
import App from 'App';

const container = document.getElementById('app');
//Before 
// Render with hydration.
ReactDOM.hydrate(<App tab="home" />, container);
//After
// Create *and* render a root with hydration.
const root = ReactDOMClient.hydrateRoot(container, <App button="add" />);

// You can later update it.
root.render(<App button="update" />);

Обратный вызов рендеринга и новый корневой API

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

import * as ReactDOMClient from 'react-dom/client';

const rootElement = document.getElementById("root");
// Don't do this
ReactDOMClient.render(<App />, rootElement, () => console.log("renderered"));
// Do this
const root = ReactDOMClient.createRoot(rootElement);
root.render(<App callback={() => console.log("renderered")} />);

Вместо этого React рекомендует использовать requestIdleCallback, setTimeout или обратный вызов ref в корне.

Краткий обзор различий между устаревшим и новым корневым API

  • Обратный вызов рендеринга был удален, а работа функции гидратации изменена.
  • Корень — это объект, и любая функция для обновления корня применяется к этому объекту, а не к DOM.

Автоматическое дозирование

Вы когда-нибудь обновляли несколько состояний в одной функции, вызываемой обработчиком событий? Вы могли заметить, что все состояния обновляются в одном рендере. Это называется автоматическим пакетированием.

Пакетная обработка — это когда React группирует несколько обновлений состояния в один повторный рендеринг для повышения производительности.

До React 18 пакетная обработка по умолчанию выполнялась только в обработчиках событий React.

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

Отключить автоматическую пакетную обработку

Если вариант использования требует обновления состояния и ожидания ответа перед выполнением другого действия, ReactDOM. flushSync()приходит на помощь.

import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => {
    // setState function
  });
  // React has updated the DOM by now
  flushSync(() => {
     // setState function
  });
  // React has updated the DOM by now
}

Рендеринг на стороне сервера Suspense/Streaming (SSR) и выборочная гидратация

На языке непрофессионала под рендерингом на стороне сервера понимается создание HTML-контента на сервере и его предоставление клиенту до тех пор, пока JavaScript не будет полностью загружен и обслужен.

Недостатки в текущей архитектуре React SSR

  • Весь JS должен быть загружен до гидратации, и тогда компоненты станут интерактивными.
  • Данные, такие как вызовы API, должны быть уже готовы на стороне сервера, так как они не будут ждать.
  • Все компоненты должны быть гидратированы перед взаимодействием с ними.

Архитектура React 18 SSR

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

1. Потоковая передача HTML

Обертывание любого компонента с помощью ‹suspense›‹/suspense›укажет React не ждать этого компонента. Вместо этого он начнет потоковую передачу HTML, и когда данные для этого кода, завернутые в Suspense, будут готовы на сервере, дополнительный HTML будет отправлен в тот же поток с минимальным количеством встроенных JS для размещения в нужном месте.

Для потоковой передачи HTML на сервер вместо renderToString следует использовать API renderToPipeableStream.

Это поможет преодолеть проблему наличия полных данных перед рендерингом чего-либо.

2. Выборочная гидратация и ленивая загрузка на SSR

Функция Lazy в React обеспечивает ленивую загрузку на стороне клиента, то есть будет загружен только JS требуемого компонента, а не все приложение.

Прежде чем использовать выборочную гидратацию, вы должны подписаться на новый API createRoot.

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

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

import { lazy } from 'react';

const Products = lazy(() => import('./products.js'));

<Suspense fallback={<Spinner />}>
  <Products />
</Suspense>

Новые API

1. Параллельный рендеринг

  • API startTransition

Работа с огромными объемами данных и обновление больших состояний часто замедляют или зависают пользовательский интерфейс.

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

import { useTransition, startTransition } from 'react';
// Urgent: Show what was typed
setInputValue(input);

// Mark any state updates inside as transitions
startTransition(() => {
  // Transition: Show the results
  setSearchQuery(input);
});

Он также предоставляет перехватчик useTransitionдля проверки состояния переходов.

import { useTransition } from 'react';

const [isPending, startTransition] = useTransition();
{isPending && <Spinner />}
  • useDeferredValue
import { useDeferredValue } from "react";
const deferredValue = useDeferredValue(text, { timeoutMs: 2000 });

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

2. идентификатор использования

useId — это API для создания уникальных идентификаторов как на клиенте, так и на сервере, избегая при этом несоответствий гидратации.

const id = useId();
<input type="checkbox" name="react" id={id} />

Улучшения в Suspense с помощью SuspenseList

SuspenseList вступает в действие, когда нескольким компонентам требуется выборка данных и непредсказуемые заказы.

SuspenseList работает только с ближайшими к нему компонентами Suspense и SuspenseList. Он не ищет границы глубже одного уровня. Однако можно вкладывать несколько компонентов SuspenseList друг в друга для создания сеток.

<SuspenseList revealOrder="forwards">
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={1} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={2} />
  </Suspense>
  <Suspense fallback={'Loading...'}>
    <ProfilePicture id={3} />
  </Suspense>
  ...
</SuspenseList>

В SuspenseList компонент не будет отображаться до тех пор, пока предыдущий не получит данные и не будет готов.

Он принимает два реквизита: revealOrder (вперед, назад, вместе) определяет порядок, в котором перечисленные компоненты должны отображаться, а хвост (свернутый, скрытый) определяет порядок выгрузки элементов в Показан SuspenseList.

Краткое содержание

Учитывая архитектурные изменения, которые приносит React 18, сообщество React взволновано. Это обеспечит лучший пользовательский интерфейс, UX, CX, а более надежные и эффективные веб-приложения уже не за горами.

Следите за обновлениями!

Подключаемся по LinkedIn.