Поиск по сайту:

Что будет в React 18?


React 18 станет следующей основной версией популярной библиотеки компонентов JavaScript. Теперь доступный в качестве кандидата на выпуск, он вносит несколько изменений для улучшения выборки данных, производительности и рендеринга на стороне сервера.

Чтобы воспользоваться всеми функциями, вам необходимо обновить проект, и вы можете столкнуться с критическими изменениями. Однако React 18 по-прежнему обратно совместим со старым кодом. У вас должна быть возможность изменить версию выпуска в вашем package.json, не сталкиваясь со слишком большим количеством немедленных проблем.

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

Мотивация большинства изменений React 18 связана с так называемым «одновременным рендерингом». Этот механизм позволяет React одновременно собирать несколько версий вашего дерева компонентов. Хотя детали этого относятся только к внутреннему устройству библиотеки, результатом является повышенная гибкость и повышенная производительность вашего приложения.

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

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

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

Параллельный режим и параллельный рендеринг

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

Параллельный рендеринг принципиально отличается от существующей системы рендеринга. Он имеет совершенно новый API, который заменяет знакомый ReactDOM.render(). Во времена параллельного режима параллелизм был принципом «все или ничего»: он либо был включен для вашего приложения с перспективой серьезных критических изменений, либо полностью запрещен. Теперь это обрабатывается более изящно: React применяет параллельный рендеринг только к обновлениям DOM, которые на самом деле требуют параллельной функции.

Новый корневой API (активация параллельного режима)

Существующие приложения, обновленные до React 18, могут продолжать использовать ReactDOM.render() в обозримом будущем. Это отобразит ваше приложение без поддержки параллелизма, используя знакомый рендерер из версии 17. Вы увидите предупреждение консоли о том, что API больше не поддерживается, но это можно не учитывать при обновлении.

import App from "./App.js";
import ReactDOM from "react-dom";
 
// "ReactDOM.render is no longer supported in React 18"
ReactDOM.render(<App />, document.getElementById("root"));

Чтобы убрать предупреждение, переключитесь на новый API createRoot():

import {createRoot} from "react-dom/client";
const root = createRoot(document.getElementById("root"));
root.render(<App />);

createRoot() возвращает новый корневой объект, представляющий поверхность рендеринга React. Вы можете вызвать его метод render() для рендеринга компонента React в корень. Результат приведенного выше кода такой же, как и в предыдущем примере ReactDOM.render(). createRoot() — это более объектно-ориентированный интерфейс с улучшенной простотой использования.

Корни, созданные с помощью createRoot(), поддерживают параллельный рендеринг. Обновление до этого API дает вам доступ к новым возможностям React 18.

createRoot() эквивалент ReactDOM.unmountComponentAtNode() — это новый метод unmount(), доступный для корневых объектов. Вы можете использовать это, чтобы отсоединить дерево компонентов React и остановить рендеринг вашего приложения:

import App from "./App.js";
import ReactDOM from "react-dom";
 
// OLD
ReactDOM.unmountComponentAtNode(document.getElementById("root"));
 
// NEW
const root = createRoot(document.getElementById("root"));
root.unmount();

Параллельные функции

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

саспенс

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

const fetchPostHistory = id => fetch(`/users/${id}/posts`);
 
const UserCard = ({Id, Name}) => {
 
    const [postHistory] = useState(() => fetchPostHistory(Id));
 
    <div>
        <h1>{Name}</h1>
        <React.Suspense fallback="Loading...">
            <UserPostHistoryList posts={postHistory} />
            <ReportUserLink id={Id} />
        </React.Suspense>
    </div>
};

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

Если вы зарегистрируете эффекты каждого компонента, вы увидите, что компонент ReportUserLink отрисовывался во время загрузки сообщений, даже если в этот момент он не виден. Используя объяснение параллелизма из более раннего, можно объяснить, почему: как только React начал рендеринг дерева компонентов, у него не было возможности остановиться, даже если человек может заметить, что ReportUserLink является избыточным, пока postHistory заполняется.

Приостановка более мощная в React 18. Новая версия называется «Конкурентная приостановка»; предыдущая реализация теперь называется Legacy Suspense. Это решает проблему в приведенном выше примере: рендеринг одного и того же кода с включенным параллелизмом не позволит средству рендеринга достичь , пока идет выборка данных.

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

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

Переходы

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

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

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

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

API startTransition() инкапсулирует обновления состояния в виде переходов:

import {startTransition} from "react";
 
const Component = () => {
 
    const [searchQuery, setSearchQuery] = useState("");
    const [searchResults, setSearchResults] = useState({});
 
    /**
     * State updates within the transition function are low-priority
     */
    startTransition(() => {
        setSearchResults({text: "Search Result 1"});
    });
 
};

Если вы хотите проверить, выполняется ли обновление, замените простой startTransition() хуком useTransition(). Это дает вам логическое значение, указывающее, есть ли незавершенная работа над переходом.

import {useTransition} from "react";
 
const Component = () => {
 
    const [searchQuery, setSearchQuery] = useState("");
    const [searchResults, setSearchResults] = useState({});
 
    const [isSearching, startSearchResultsTransition] = useTransition();
 
    startSearchResultsTransition(() => {
        setSearchResults({text: "Search Result 1"});
    });
 
    return (
        <div>
            <input onChange={setSearchQuery} value={searchQuery} />
            <SearchResults results={searchResults} />
            {(isSearching && "(Searching...)")}
        </div>
    );
 
};

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

Отложенные значения

Отложенные значения — это еще один способ поддержания скорости отклика во время длительных обновлений. Когда значение отложено хуком useDeferredValue(), React будет продолжать показывать его старое значение в течение указанного периода.

const Component = () => {
    const [results, setResults] = useState([]);
    const deferredResults = useDeferredResults(results, {timeoutMs: 5000});
    return <ResultsGrid results={deferredResults} />;
};

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

Лучшее дозирование

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

const Component = () => {
 
    const [query, setQuery] = useState("");
    const [queryCount, setQueryCount] = useState("");
 
    /**
     * Two state updates, only one re-render
     */
    setQuery("demo");
    setQueryCount(queryCount + 1);
 
};

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

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

const Component = () => {
 
    const [query, setQuery] = useState("");
    const [queryCount, setQueryCount] = useState("");
 
    const handleSearch = query => {
       fetch(query).then(() => {
 
            /**
             * Force commit and update the DOM
             */
            flushSync(() => setQuery(query));
 
            setQueryCount(1);
 
        });
    }
 
};

Изменения рендеринга на стороне сервера

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

Вследствие этого изменения некоторые API устарели или были переработаны, в том числе renderToNodeStream(). Теперь вам следует использовать renderToPipeableStream() или renderToReadableStream() для доставки содержимого на стороне сервера, совместимого с современными потоковыми средами.

Гидратация содержимого, отображаемого на сервере, на стороне клиента также изменилась, чтобы соответствовать новому API одновременного рендеринга. Если вы используете серверный рендеринг и параллельный режим, замените hydra() на hydraRoot():

// OLD
import {hydrate} from "react-dom";
hydrate(<App />, document.getElementById("root"));
 
// NEW
import {hydrateRoot} from "react-dom/client";
hydrateRoot(document.getElementById("root"), <App />);

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

Заключение

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

Обновление до React 18 после его выпуска в большинстве случаев должно быть довольно безболезненным. Вы можете продолжать использовать корневой API React 17 до тех пор, пока не перейдете на createRoot(), когда будете готовы принять параллельный рендеринг. Если вы хотите начать подготовку своего приложения сегодня, вы можете установить последнюю версию-кандидат, запустив npm install react@rc react-dom@rc.




Все права защищены. © Linux-Console.net • 2019-2024