React — это библиотека JavaScript с открытым исходным кодом, которую разработчики используют для создания интерактивных пользовательских интерфейсов и компонентов пользовательского интерфейса для веб-приложений и мобильных приложений. Созданная Джорданом Уоке, инженером-программистом Facebook, первая версия React вышла семь лет назад. Теперь Facebook поддерживает его. С момента первого выпуска популярность React резко возросла.

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

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

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

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

Почему одновременный режим?

Фреймворки или библиотеки JavaScript работают с одним потоком. Таким образом, когда блок кода выполняется, остальные блоки должны ожидать выполнения. Они не могут выполнять какую-либо работу одновременно. Эта концепция применима и к рендерингу.

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

Проблема

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

Если список длинный, UI «заикается», то есть рендеринг виден пользователю. Заикание также снижает производительность. Разработчики могут использовать несколько методов, таких как регулирование и устранение дребезга, которые немного помогают, но не являются решением.

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

Debouncing игнорирует вызовы функции в течение заданного периода времени. Вызов функции осуществляется только по истечении заданного времени.

Вы можете увидеть заикание на изображении ниже.

В ожидании несрочных вызовов API для завершения пользовательский интерфейс заикается, что блокирует отрисовку пользовательского интерфейса. Решение — прерываемый рендеринг с использованием Concurrent Mode.

Прерываемый рендеринг

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

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

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

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

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

Используя параллельный режим, мы можем:

  1. Управление первым процессом рендеринга
  2. Приоритизируйте процессы рендеринга
  3. Приостановка и возобновление рендеринга компонентов
  4. Кэширование и оптимизация рендеринга компонентов во время выполнения
  5. Скрыть содержимое с экрана до тех пор, пока оно не потребуется

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

Использование хука Deferred Value

Синтаксис хука useDeferredValue:

const deferredValue = useDeferredValue(value, { timeoutMs: <some value> });

Эта команда заставляет значение «отставать» на время, установленное в timeoutMs. Команда сохраняет пользовательский интерфейс реактивным и отзывчивым, независимо от того, должен ли пользовательский интерфейс обновляться немедленно или должен ждать данных. Этот хук предотвращает заикание пользовательского интерфейса и поддерживает постоянную отзывчивость пользовательского интерфейса при незначительной стоимости задержки извлеченных данных.

Использование переходного хука

Хук useTransition — это хук React, используемый в основном с Suspense. Рассмотрим следующий сценарий, в котором есть веб-страница с кнопками с именами пользователей. Одним нажатием кнопки веб-страница отображает информацию о пользователе на экране.

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

Метод useTransition возвращает значения двух хуков: startTransition и isPending. Синтаксис метода useTransition:

const [startTransition, isPending] = useTransition({ timeoutMs: 3000 });

Синтаксис startTransition:

<button disabled={isPending} 
startTransition(() => { 
      <fetch Calls> 
}); 
</button> 
{isPending? " Loading...": null}

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

Приостановка получения данных

Suspense — еще одна экспериментальная функция, которую React представила вместе с Concurrent Mode. Приостановка позволяет компонентам ожидать в течение заданного периода времени перед их визуализацией.

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

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

Приостановка позволяет:

  1. Интеграция между библиотеками выборки данных и компонентами React
  2. Управление визуальными состояниями загрузки
  3. Избегайте условий гонки

Базовый синтаксис Компонента Spinner следующий:

import Spinner from './Spinner';
  <Suspense fallback={<Spinner />}> 
    <SomeComponent /> 
  </Suspense>

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

Подвеска и ленивые компоненты

React.lazy — это новая функция, которая позволяет React.js лениво загружать компоненты. Ленивая загрузка означает, что компоненты загружаются (их код извлекается и отображается) только тогда, когда это необходимо. Они отдают приоритет наиболее важным компонентам пользовательского интерфейса. Разработчики React рекомендуют оборачивать ленивые компоненты внутрь компонента Suspense.

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

Включение параллельного режима

Чтобы включить параллельный режим, установите экспериментальную версию реакции. Необходимым условием для установки React является диспетчер пакетов узла (npm). Чтобы установить экспериментальную версию, введите команду в командной строке.

npm install react@experimental react-dom@experimental

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

import * as React from 'react'; 
  import { render } from 'react-dom'; 
  render(<App />, document.getElementById('root'));

В параллельном режиме напишите этот код:

import * as React from 'react'; 
  import { createRoot } from 'react-dom'
  createRoot(document.getElementById('root')).render(<App />);

Это включает параллельный режим для всего приложения.

React разбивает вызов рендеринга на две части:

  1. Создайте корневой элемент
  2. Используйте вызов рендеринга

В настоящее время React планирует поддерживать три режима:

  1. Legacy Mode — это традиционный или текущий режим для обратной совместимости.
  2. Режим блокировки — это промежуточный этап разработки параллельного режима.
  3. Параллельный режим

Режим блокировки использует вызов createBlockingRoot вместо вызова createRoot. Режим блокировки дает разработчикам возможность исправлять ошибки и решать проблемы во время разработки в параллельном режиме.

Документация React утверждает, что каждый режим поддерживает следующие функции:

Пример приложения

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

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

Основной файл для создания приложения пикселей — canvas.js. Мы также сделали поле ввода, где пользователь может ввести что угодно. Каждое нажатие клавиши повторно отображает холст пикселей.

Файлы кода

Index.js

import React from "react"; 
  import ReactDOM from "react-dom"; 
  import App from "./App"; 
  // Traditional or non-Concurrent Mode react 
  const rootTraditional = document.getElementById("root");  
  ReactDOM.render(<App caption="Traditional or Block Rendering" />,
  rootTraditional);  
  // Concurrent Mode enabled
  const rootConcurrent = document.getElementById("root-concurrent");   
  ReactDOM.createRoot(rootConcurrent).
  render(<App caption="Interruptible Rendering" />);

App.js

 import React, { useState, useDeferredValue } from "react"; 
 import "./App.css"; 
 import { Canvas } from "./Canvas"; 
 export default function App(props) 
 { const [value, setValue] = useState(""); 
//This is available only in the Concurrent mode. 
   const deferredValue = useDeferredValue(value, { 
     timeoutMs: 5000 
   }); 
   const keyPressHandler = e => { 
     setValue(e.target.value); 
   }; 
   return ( 
     <div className="App"> 
       <h1>{props.caption}</h1> 
       <input onKeyUp={keyPressHandler} /> 
       <Canvas value={deferredValue} /> 
     </div> 
   ); 
 }

Canvas.js

import React from "react";      
 const CANVAS_SIZE = 70;      
 const generateRandomColor = () => {       
  var letters = "0123456789ABCDEF";       
  var color = "#";       
  for (var i = 0; i < 6; i++) {         
    color += letters[Math.floor(Math.random() * 16)];       
  }       
  return color;     
};      
 const createCanvas = (rows, columns) => {       
  let array = [];       
  for (let i = 0; i < rows; i++) {         
    let row = [];         
    for (let j = 0; j < columns; j++) {           
      row.push(0);         
    }         
    array.push(row);       
  }       
  return array;     
};      
 //This is the square with the pixels     
const drawCanvas = value => {       
  const canvas = createCanvas(CANVAS_SIZE, CANVAS_SIZE);       
  return canvas.map((row, rowIndex) => {         
    let cellsArrJSX = row.map((cell, cellIndex) => {           
      let key = rowIndex + "-" + cellIndex;           
      return (            
      <div               
         style={{ backgroundColor: generateRandomColor() }}                          
         className="cell"            
         key={"cell-" + key}             
       />           
     );         
   });         
   return (           
     <div key={"row-" + rowIndex} className="canvas-row">                  
       {cellsArrJSX}      
     </div>         
   );       
 });     
};     
export const Canvas = ({ value }) => {       
  return (        
   <div>           
      <h2 style={{ minHeight: 30 }}>{value}</h2>          
     <div className="canvas">{drawCanvas(value)}</div>       
   </div>      
 );    
};

Индекс.html

<!DOCTYPE html>     
<html lang="en">       
  <head>         
    <meta charset="utf-8" />         
    <meta          
      name="viewport"           
      content="width=device-width, initial-scale=1, shrink-to-fit=no"         
    />         
    <meta name="theme-color" content="#000000" />         
    <title>React App Concurrent Mode</title>      
  </head>       
  <body>         
    <noscript>        
  You need to enable JavaScript to run this app.         
    </noscript>         
    <div id="container">           
      <div id="root" class="column"></div>           
      <div id="root-concurrent" class="column"></div>         
    </div>       
   </body>     
 </html>

Запуск кода

Давайте посмотрим на наш код в действии. Первый экран, который мы видим, является начальным экраном. Использование традиционного или блочного рендеринга — вот как React делает это сегодня. Прерываемый рендеринг — это экспериментальная функция параллельного рендеринга. Сначала мы рассмотрим работу традиционного рендеринга.

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

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

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

Резюме

В этой статье мы рассмотрели экспериментальные функции React, Concurrent Mode и Suspense. Используя Concurrent Mode, React.js всегда поддерживает отзывчивость пользовательского интерфейса.

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

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

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

Первоначально опубликовано на https://www.grapecity.com 2 июня 2021 г.