Как работает 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
работает только с динамическим импортом, он же ленивая загрузка. Если вы не уверены, когда его использовать, следуйте этому руководству:
- Начните с уровня маршрутизатора. Посмотрите это руководство, в котором показано, как реализовать React.Suspense и React.lazy() для добавления ленивой загрузки в React router.
- Разделяйте только те фрагменты приложений React, которые не являются сверхкритичными для пользователя.
В ближайшем будущем React, React.Suspense
будет использоваться не только для ленивой загрузки, но и для выборки данных.
Так же с особенностями работы React Suspense могут помочь наставники Teacher Army.
Переходите по ссылке и оставляйте заявку, а мы подберем вам подходящего ментора по программированию, который на индивидуальном созвоне объяснит любую тему.