Как работает React Suspense

Tweet about react suspense

React Suspense - это компонент React, который приостанавливает отрисовку компонентов до выполнения определенного условия и отображает запасной вариант - fallback. Этот запасной вариант является обязательным, и это может быть строка или другой компонент, например, спиннер. Так же React Suspense работает только с динамическим импортом, он же ленивая загрузка.

// Dynamic import
const CatAvatar = React.lazy(() => import('./path/to/cat/avatar'));

// Displays "loading..." to the user until CatAvatar has finished loading.
const AppContainer = () => (
  <React.Suspense fallback="loading...">
    <CatAvatar />
  </React.Suspense>
);

Почему React Suspense полезен?

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

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

React может работать с несколькими React-компонентами

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

const CatAvatar = React.lazy(() => import('./path/to/cat/avatar'));
const ThorAvatar = React.lazy(() => import('./path/to/cat/thor-avatar'));

const AppContainer = () => (
  <React.Suspense fallback="loading...">
    <CatAvatar />
    <ThorAvatar />
  </React.Suspense>
);

Так же вы можете иметь несколько вложенных компонентов React.lazy внутри React.Suspense.

Обработка ошибок в React.Suspense и React.lazy

С React.lazy и React.Suspense может возникнуть несколько проблем. Интернет-соединение оборвалось в процессе загрузки. Недостаточная мощность устройства. Неправильное имя пути к файлу компонента React. Как же мы справляемся со сбоями загрузки?

У React есть стандартный шаблон для изящной обработки ошибок. Они раскрыли 2 жизненных цикла React, статический getDerivedStateFromError() и componentDidCatch(). Эти два жизненных цикла похожи тем, что оба срабатывают при возникновении ошибки в дочернем компоненте. Разница заключается в следующем - static getDerivedStateFromError() требует, чтобы вы вернули объект для обновления состояния. Что касается componentDidCatch(), то это метод, который ничего не возвращает и вам придется вызвать useState() или setState() для обновления состояния компонента.

Я собираюсь добавить к приведенному выше примеру React.Suspense.

const CatAvatar = React.lazy(() => import('./path/to/cat/avatar'));

class ErrorHandler extends React.Component {
  state = {
    hasError: false,
  };

  static getDerivedStateFromError(err) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return (
        <p>
          Loading has failed. Try refreshing the browser!
        </p>
      );
    }

    return this.props.children;
  }
}

const AppContainer = () => (
  <ErrorHandler>
    <React.Suspense fallback="loading...">
      <CatAvatar />
    </React.Suspense>
  </ErrorHandler>
);

Если при загрузке CatAvatar произошла ошибка, будет выведено сообщение об ошибке.

React Suspense для получения данных

Как было показано выше, React.Suspense работает только с динамическим импортом. Он предназначен только для поддержки ленивой загрузки. Но в настоящее время команда React упорно работает над тем, чтобы React.Suspense работал и с выборкой данных. React.Suspense связан с новым режимом, над которым работает React, под названием Concurrent Mode.

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

Во-первых, чтобы включить Concurrent Mode, необходимо установить экспериментальную версию React.

npm install --save react@0.0.0-experimental-5faf377df react-dom@0.0.0-experimental-5faf377df

Второй шаг заключается в замене того, как мы прикрепляем приложение React к оболочке HTML.

// Current
ReactDOM.render(<App />, document.getElementById('root'));

// Concurrent Mode
ReactDOM
  .createRoot(document.getElementById('root'))
  .render(<App />);

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

React.SuspenseList - API

Еще одна новая функция, которая была добавлена в Concurrent Mode - React SuspenseList. Это компонент React, который принимает несколько компонентов React.Suspense и позволяет вам организовать их раскрытие.

// Cat 1 fetch time: 45ms
// Cat 2 fetch time: 100ms
// Cat 3 fetch time: 10ms

// Отображает всех дочерних элементов, когда выборка всех данных завершена.
<React.SuspenseList revealOrder="together">
  <React.Suspense fallback={'Loading first cat...'}>
    <Cat id={1} />
  </React.Suspense>
  <React.Suspense fallback={'Loading second cat...'}>
    <Cat id={2} />
  </React.Suspense>
  <React.Suspense fallback={'Loading third cat...'}>
    <Cat id={3} />
  </React.Suspense>
</React.SuspenseList>

// Всегда начинается сверху вниз: Cat 1, Cat 2, Cat 3
<React.SuspenseList revealOrder="forward">
  <React.Suspense fallback={'Loading first cat...'}>
    <Cat id={1} />
  </React.Suspense>
  <React.Suspense fallback={'Loading second cat...'}>
    <Cat id={2} />
  </React.Suspense>
  <React.Suspense fallback={'Loading third cat...'}>
    <Cat id={3} />
  </React.Suspense>
</React.SuspenseList>

// Всегда начинается снизу вверх: Cat 3, Cat 2, Cat 1
<React.SuspenseList revealOrder="backwards">
  <React.Suspense fallback={'Loading first cat...'}>
    <Cat id={1} />
  </React.Suspense>
  <React.Suspense fallback={'Loading second cat...'}>
    <Cat id={2} />
  </React.Suspense>
  <React.Suspense fallback={'Loading third cat...'}>
    <Cat id={3} />
  </React.Suspense>
</React.SuspenseList>

React.SuspenseList работает только с ближайшими компонентами React.Suspense. Не более чем на один уровень вглубь.

Заключение

На данный момент React.Suspense работает только с динамическим импортом, он же ленивая загрузка. Если вы не уверены, когда его использовать, следуйте этому руководству:

  1. Начните с уровня маршрутизатора. Посмотрите это руководство, в котором показано, как реализовать React.Suspense и React.lazy() для добавления ленивой загрузки в React router.
  2. Разделяйте только те фрагменты приложений React, которые не являются сверхкритичными для пользователя.

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


Так же с особенностями работы React Suspense могут помочь наставники Teacher Army.

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