Информация по асинхронной отрисовке компонентов
27 Марта, 2018. Brian Vaughn (Брайан Вон)
Уже более года команда React работает над реализацией асинхронной отрисовки. Теперь мы хотели бы поделиться с вами некоторыми уроками, которые усвоили, работая над этими функциями, а также некоторыми рекомендациями, которые помогут подготовить ваши компоненты для асинхронной отрисовки, когда она будет активирована.
Один из самых больших уроков, который мы усвоили, заключается в том, что некоторые из наших устаревших методов жизненного цикла компонента, склонны поощрять небезопасные практики кодирования. Это:
componentWillMount
componentWillReceiveProps
componentWillUpdate
Эти методы жизненного цикла часто понимались неправильно и злоупотреблялись. Более того, мы ожидаем, что их потенциальное злоупотребление может принести еще больше проблем вместе с асинхронной отрисовкой. Из-за этого мы добавим префикс «UNSAFE_» к данным методам в предстоящей версии. (Здесь префикс «UNSAFE_» относится не к безопасности. Он сообщает, что код, использующий данные методы, будет с большей вероятностью иметь ошибки в будущих версиях React, особенно после активации асинхронной отрисовки.)
React следует схеме управления версиями, поэтому данное изменение будет постепенным. Наш текущий план:
- Релиз 16.3: Вводит псевдонимы/алиасы для небезопасных методов жизненного цикла,
UNSAFE_componentWillMount
,UNSAFE_componentWillReceiveProps
иUNSAFE_componentWillUpdate
. (В данном релизе будут работать и старые имена методов жизненного цикла, и новые псевдонимы.) - Будущие релизы 16.x: Будут включать предупреждение об устаревании
методов
componentWillMount
,componentWillReceiveProps
иcomponentWillUpdate
. (В данных релизах будут работать и старые имена методов жизненного цикла, и новые псевдонимы, но старые имена будут выводить предупреждение в режиме разработки.) - Релиз 17.0: Удалит методы
componentWillMount
,componentWillReceiveProps
иcomponentWillUpdate
. (С того момента будут работать только новыеUNSAFE_
имена методов жизненного цикла).
Обратите внимание: если вы являетесь разработчиком приложения React, вам не нужно ничего делать в отношении устаревших методов. Главная цель предстоящего релиза версии 16.3 заключается в том, чтобы позволить разработчикам проектов с открытым исходным кодом обновлять свои библиотеки до возникновения любых предупреждений об устаревании. Данные предупреждения не будут активированы вплоть до следующего выпуска 16.x.
Мы поддерживаем более 50 000 компонентов React в Facebook, и мы не планируем сразу их всех переписывать. Мы понимаем, что миграция требует времени. Мы будем проходить постепенный путь миграции вместе со всеми в сообществе React.
Если вы хотите начать использовать новый API компонентов, представленный в React 16.3 (или если вы являетесь разработчиком, который хочет обновить свою библиотеку заранее), вот несколько примеров, которые, как мы надеемся, помогут вам посмотреть на компоненты под другим углом. Со временем мы планируем добавить дополнительные «рецепты» к нашей документации, которые покажут, как можно выполнять общие задачи таким образом, чтобы можно было избежать использования проблемных методов жизненного цикла.
Прежде чем мы начнем, кратко рассмотрим изменения жизненного цикла, запланированные для версии 16.3:
- Мы добавляем следующие псевдонимы методов жизненного цикла:
UNSAFE_componentWillMount
,UNSAFE_componentWillReceiveProps
иUNSAFE_componentWillUpdate
. (Будут поддерживаться как старые имена методов жизненного цикла, таки и новые псевдонимы.) - Мы представляем два новых метода жизненного цикла:
статические
getDerivedStateFromProps
иgetSnapshotBeforeUpdate
.
Новый метод жизненного цикла: getDerivedStateFromProps
class Example extends React.Component {
static getDerivedStateFromProps(props, state) {
// ...
}
}
Новый статический метод жизненного цикла getDerivedStateFromProps
запускается
после создания экземпляра компонента, перед его повторной отрисовкой. Он может
вернуть объект для обновления состояния state
или null
, чтобы указать, что новые
свойства props
не требуют каких-либо обновлений состояния state
.
Вместе с componentDidUpdate
данный новый метод жизненного цикла должен охватывать
все случаи использования устаревшего componentWillReceiveProps
.
Внимание!
Как устаревшийcomponentWillReceiveProps
, так и новый getDerivedStateFromProps
методы придают значительную сложность компонентам. Это часто приводит к ошибкам.
Рассмотрим более простые альтернативы
производному состоянию, чтобы сделать компоненты предсказуемыми и поддерживаемыми.
Новый метод жизненного цикла: getSnapshotBeforeUpdate
Новый метод жизненного цикла getSnapshotBeforeUpdate
вызывается непосредственно перед
произведением мутаций (например, перед обновлением DOM). Возвращаемое значение для данного
метода жизненного цикла будет передано в качестве третьего параметра в componentDidUpdate
.
(Данный метод жизненного цикла нужен не так часто, но может быть полезен в таких случаях,
как ручное сохранение положения прокрутки во время перерисовок).
Вместе с componentDidUpdate
данный новый метод жизненного цикла должен охватывать
все случаи использования устаревшего componentWillUpdate
.
Далее мы рассмотрим примеры использования данных методов ЖЦ.
- Инициализация состояния
- Получение внешних данных
- Добавление слушателей событий (или подписок)
- Обновление состояния state на основе свойств props
- Вызов внешних коллбэков
- Побочные эффекты при изменении свойств props
- Получение внешних данных при изменении свойств props
- Чтение DOM свойств перед обновлением
Внимание!
Для краткости приведенные ниже примеры написаны с использованием трансформации экспериментальных свойств класса, но те же стратегии миграции применимы и без этого.Инициализация состояния
В данном примере показан компонент с вызовом setState
внутри componentWillMount
:
// До
class ExampleComponent extends React.Component {
state = {};
componentWillMount() {
this.setState({
currentColor: this.props.defaultColor,
palette: 'rgb',
});
}
}
Простейшим рефакторингом для такого случая является перенос инициализации состояния в конструктор или инициализатор свойств, например:
// После
class ExampleComponent extends React.Component {
state = {
currentColor: this.props.defaultColor,
palette: 'rgb',
};
}
Получение внешних данных
Ниже приведен пример компонента, который использует
componentWillMount
для извлечения внешних данных:
// До
class ExampleComponent extends React.Component {
state = {
externalData: null,
};
componentWillMount() {
this._asyncRequest = asyncLoadData().then(
externalData => {
this._asyncRequest = null;
this.setState({externalData});
}
);
}
componentWillUnmount() {
if (this._asyncRequest) {
this._asyncRequest.cancel();
}
}
render() {
if (this.state.externalData === null) {
// Отопражаем лоудер (картинку-гифку) ...
} else {
// Отображаем UI, с данными ...
}
}
}
Вышеприведенный код проблематичен как для серверной отрисовки (где внешние данные не будут использоваться), так и для предстоящего режима асинхронной отрисовки (где запрос может быть инициирован множество раз).
Рекомендуемый путь апгрейда для большинства ситуаций - это перенос
логики извлечения данных в componentDidMount
:
// После
class ExampleComponent extends React.Component {
state = {
externalData: null,
};
componentDidMount() {
this._asyncRequest = asyncLoadData().then(
externalData => {
this._asyncRequest = null;
this.setState({externalData});
}
);
}
componentWillUnmount() {
if (this._asyncRequest) {
this._asyncRequest.cancel();
}
}
render() {
if (this.state.externalData === null) {
// Отопражаем лоудер (картинку-гифку) ...
} else {
// Отображаем UI, с данными ...
}
}
}
Существует распространенное заблуждение, что логика извлечения данных в
componentWillMount
позволяет избежать отображения пустого состояния при первой отрисовке.
На практике это никогда не соответствовало действительности, потому что React всегда выполнял
отрисовку сразу после componentWillMount
. Если данные не доступны к моменту
срабатывания componentWillMount
, первая отрисовка будет по-прежнему показывать
состояние загрузки независимо от того, где вы инициируете извлечение данных. Вот
почему перенос fetch
в componentDidMount
не дает ощутимой разницы в подавляющем
большинстве случаев.
Внимание!
В некоторых продвинутых случаях использования (например, библиотеки, такие как Relay), возможно, захочется поэкспериментировать с предварительной асинхронной выборкой данных. Пример того, как это можно сделать, .В более долгосрочной перспективе канонический способ получения данных в компонентах React, скорее всего, будет основан на API-интерфейсе «приостановка», представленном на JSConf Iceland. Как решения по простому извлечению данных, так и библиотеки, такие как Apollo или Relay, смогут использовать его у себя под капотом. Он наименее многословен, чем любое из вышеперечисленных решений, но, к сожалению, не будет завершен к моменту выпуска 16.3.
В настоящее время при поддержке отрисовки на сервере необходимо предоставлять данные синхронно - ранее для этой цели часто использовался
componentWillMount
, но в качестве
замены можно использовать и конструктор. Предстоящий API-интерфейс приостановки сделает
асинхронную выборку данных возможной (и в аккуратной форме) как для серверной, так и для клиентской отрисовок.
Добавление слушателей событий (или подписок)
Ниже приведен пример компонента, который подписывается на диспетчер внешних событий при монтировании:
// До
class ExampleComponent extends React.Component {
componentWillMount() {
this.setState({
subscribedValue: this.props.dataSource.value,
});
// Это небезопасно! Может привести к утечке памяти!
this.props.dataSource.subscribe(
this.handleSubscriptionChange
);
}
componentWillUnmount() {
this.props.dataSource.unsubscribe(
this.handleSubscriptionChange
);
}
handleSubscriptionChange = dataSource => {
this.setState({
subscribedValue: dataSource.value,
});
};
}
К сожалению, это может привести к утечкам памяти как для случая отрисовки на сервере
(где componentWillUnmount
никогда не будет вызван), так и для асинхронной отрисовки
(где отрисовка может быть прервана до своего завершения, в результате чего
componentWillUnmount
не будет вызван).
Люди часто предполагают, что componentWillMount
и componentWillUnmount
всегда сопряжены,
но это не гарантируется. Только единожды, после того как был вызван componentDidMount
,
React гарантирует, что componentWillUnmount
будет вызван позже для очистки.
По этой причине рекомендуемым способом добавления слушателей/подписок является
использование метода жизненного цикла componentDidMount
:
// После
class ExampleComponent extends React.Component {
state = {
subscribedValue: this.props.dataSource.value,
};
componentDidMount() {
// Слушателей событий можно безопасно добавлять только после монтирования,
// Таким образом, они не будут приводить к утечкам, если монтирование будет прервано или из-за ошибок.
this.props.dataSource.subscribe(
this.handleSubscriptionChange
);
// Внешние значения могут изменяться между отрисовкой и монтированием.
// В некоторых случаях может быть важно обработать такой случай.
if (
this.state.subscribedValue !==
this.props.dataSource.value
) {
this.setState({
subscribedValue: this.props.dataSource.value,
});
}
}
componentWillUnmount() {
this.props.dataSource.unsubscribe(
this.handleSubscriptionChange
);
}
handleSubscriptionChange = dataSource => {
this.setState({
subscribedValue: dataSource.value,
});
};
}
Иногда важно обновлять подписки в ответ на изменения свойств. Если вы используете библиотеку, такую как Redux или MobX, компонент-контейнер библиотеки должен обрабатывать это за вас. Для разработчиков приложений мы создали небольшую библиотеку, , чтобы помочь с этим. Мы опубликуем ее вместе с React 16.3.
import {createSubscription} from 'create-subscription';
const Subscription = createSubscription({
getCurrentValue(sourceProp) {
// Возвращает текущее значение подписки (sourceProp).
return sourceProp.value;
},
subscribe(sourceProp, callback) {
function handleSubscriptionChange() {
callback(sourceProp.value);
}
// Подпишитесь (например, добавьте слушатель событий) на события (sourceProp).
// Вызовите callback(newValue) при каждом изменении подписки.
sourceProp.subscribe(handleSubscriptionChange);
// Верните метод unsubscribe.
return function unsubscribe() {
sourceProp.unsubscribe(handleSubscriptionChange);
};
},
});
// Вместо того, чтобы передавать источник подписки в наш ExampleComponent,
// Мы могли бы просто передать значение подписки напрямую:
<Subscription source={dataSource}>
{value => <ExampleComponent subscribedValue={value} />}
</Subscription>;
Внимание!
Библиотекам, таким как Relay/Apollo, следует вручную управлять подписками с помощью тех же методов, что и использует у себя под капотом (как показано ), а также таким образом, который наиболее оптимален для использования в данной библиотеке.Обновление состояния state на основе свойств props
Внимание!
Как старыйcomponentWillReceiveProps
, так и новый getDerivedStateFromProps
методы добавляют значительную долю сложности компонентам. Это часто приводит к ошибкам.
Рассмотрите более простые альтернативы производному
состоянию, чтобы сделать компоненты предсказуемыми и поддерживаемыми.
Ниже приведен пример компонента, который использует устаревший метод ЖЦ
componentWillReceiveProps
для обновления состояния на основе новых
значений свойств props:
// До
class ExampleComponent extends React.Component {
state = {
isScrollingDown: false,
};
componentWillReceiveProps(nextProps) {
if (this.props.currentRow !== nextProps.currentRow) {
this.setState({
isScrollingDown: nextProps.currentRow > this.props.currentRow,
});
}
}
}
Хотя вышеупомянутый код не содержит проблем, метод ЖЦ
componentWillReceiveProps
часто используется неправильно, что создает
разные проблемы. По этой причине командой принято решение сделать его устаревшим.
Начиная с версии 16.3, рекомендуемый способ обновления состояния в ответ на изменения
свойств props
связан с новым статическим методом ЖЦ getDerivedStateFromProps
.
(Данный метод вызывается, после того как компонент был создан и каждый раз,
когда он получает новые свойства):
// После
class ExampleComponent extends React.Component {
// Инициализируйте состояние в конструкторе,
// либо с помощью инициализатора свойств.
state = {
isScrollingDown: false,
lastRow: null,
};
static getDerivedStateFromProps(props, state) {
if (props.currentRow !== state.lastRow) {
return {
isScrollingDown: props.currentRow > state.lastRow,
lastRow: props.currentRow,
};
}
// Возвращает значение null, чтобы показать, что изменения состояния не было.
return null;
}
}
В приведенном выше примере вы можете заметить, что props.currentRow
отражен в
состоянии (как state.lastRow
). Это позволяет getDerivedStateFromProps
получить
доступ к предыдущему значению свойства таким же образом, как это
сделано в componentWillReceiveProps
.
Вы могли задаться вопросом, почему мы просто не передаем предыдущие свойства в
качестве параметра getDerivedStateFromProps
. Мы рассматривали такой вариант при
разработке API, но в конечном итоге выступили против него по двум причинам:
- Параметр
prevProps
был бы нулевым при первом вызовеgetDerivedStateFromProps
(после создания экземпляра), что требует добавление проверкиif-not-null
для доступа кprevProps
в любой момент времени. - Не передавать предыдущие свойства этой функции - это шаг к освобождению памяти в
будущих версиях React. (Если React не нужно передавать предыдущие свойства методам ЖЦ,
то тогда ему не нужно хранить предыдущий объект
props
в памяти.)
Внимание!
Если вы пишете компонент для общего использования, полифилreact-lifecycles-compat
позволяет использовать новый метод ЖЦ getDerivedStateFromProps
в старых версиях React.
Подробнее о том, как его использовать будет показано ниже.
Вызов внешних коллбэков
Ниже приведен пример компонента, который вызывает внешнюю функцию при изменении своего внутреннего состояния:
// До
class ExampleComponent extends React.Component {
componentWillUpdate(nextProps, nextState) {
if (this.state.someStatefulValue !== nextState.someStatefulValue) {
nextProps.onChange(nextState.someStatefulValue);
}
}
}
Иногда люди используют componentWillUpdate
из-за неуместного опасения, что якобы
к моменту срабатывания componentDidUpdate
, будет «слишком поздно» обновлять состояние
других компонентов. Но это не тот случай. React гарантирует, что любые вызовы setState
,
которые происходят внутри componentDidMount
и componentDidUpdate
, будут произведены
до того, как пользователь увидит обновленный интерфейс. В общем, лучше избегать таких
каскадных обновлений, как это, хотя в некоторых случаях они бывают необходимы (например, если
вам нужно спозиционировать всплывающую подсказку после измерения отображённого DOM элемента).
Тем не менее, небезопасно использовать componentWillUpdate
для этой цели в
асинхронном режиме, поскольку внешний коллбэк для одного обновления может быть вызван несколько раз.
Вместо него должен использоваться метод ЖЦ componentDidUpdate
, так
как он гарантированно будет вызываться только один раз для одного обновления:
// После
class ExampleComponent extends React.Component {
componentDidUpdate(prevProps, prevState) {
if (this.state.someStatefulValue !== prevState.someStatefulValue) {
this.props.onChange(this.state.someStatefulValue);
}
}
}
Побочные эффекты при изменении свойств props
Как и в примере выше, иногда у компонентов есть побочные эффекты,
когда свойства props
изменяется.
// До
class ExampleComponent extends React.Component {
componentWillReceiveProps(nextProps) {
if (this.props.isVisible !== nextProps.isVisible) {
logVisibleChange(nextProps.isVisible);
}
}
}
Как и componentWillUpdate
, метод componentWillReceiveProps
может вызываться
несколько раз для одного обновления. По этой причине важно избегать
появления побочных эффектов в данном методе. Вместо него должен использоваться
метод componentDidUpdate
, поскольку он гарантированно будет вызываться
только один раз за одно обновление:
// После
class ExampleComponent extends React.Component {
componentDidUpdate(prevProps, prevState) {
if (this.props.isVisible !== prevProps.isVisible) {
logVisibleChange(this.props.isVisible);
}
}
}
Получение внешних данных при изменении свойств props
Ниже приведен пример компонента, который извлекает внешние
данные на основе значений из props
:
// До
class ExampleComponent extends React.Component {
state = {
externalData: null,
};
componentDidMount() {
this._loadAsyncData(this.props.id);
}
componentWillReceiveProps(nextProps) {
if (nextProps.id !== this.props.id) {
this.setState({externalData: null});
this._loadAsyncData(nextProps.id);
}
}
componentWillUnmount() {
if (this._asyncRequest) {
this._asyncRequest.cancel();
}
}
render() {
if (this.state.externalData === null) {
// Отображение состояния загрузки...
} else {
// Отображения UI с данными...
}
}
_loadAsyncData(id) {
this._asyncRequest = asyncLoadData(id).then(
externalData => {
this._asyncRequest = null;
this.setState({externalData});
}
);
}
}
Рекомендуемый путь апгрейда для этого компонента - перенос обновлений
данных в componentDidUpdate
. Вы также можете использовать новый метод
ЖЦ getDerivedStateFromProps
для очистки устаревших данных перед
отрисовкой новых свойств:
// После
class ExampleComponent extends React.Component {
state = {
externalData: null,
};
static getDerivedStateFromProps(props, state) {
// Сохраняем prevId в состояние, чтобы мы могли его сравнивать при изменении свойств.
// Очищаем ранее загруженные данные (чтобы не отображать устаревшую информацию).
if (props.id !== state.prevId) {
return {
externalData: null,
prevId: props.id,
};
}
// Нет необходимости обновлять состояние
return null;
}
componentDidMount() {
this._loadAsyncData(this.props.id);
}
componentDidUpdate(prevProps, prevState) {
if (this.state.externalData === null) {
this._loadAsyncData(this.props.id);
}
}
componentWillUnmount() {
if (this._asyncRequest) {
this._asyncRequest.cancel();
}
}
render() {
if (this.state.externalData === null) {
// Отображение состояния загрузки...
} else {
// Отображения UI с данными...
}
}
_loadAsyncData(id) {
this._asyncRequest = asyncLoadData(id).then(
externalData => {
this._asyncRequest = null;
this.setState({externalData});
}
);
}
}
Внимание!
Если вы используете HTTP-библиотеку, которая поддерживает отмену, например, axios, то при демонтировании очень просто отменить запрос, находящийся в состоянии выполнения. При использовании нативных Promise вы можете использовать подход, подобный .Чтение DOM свойств перед обновлением
Ниже приведен пример компонента, который считывает свойство из DOM перед обновлением, для поддержки положения прокрутки в списке:
class ScrollingList extends React.Component {
listRef = null;
previousScrollOffset = null;
componentWillUpdate(nextProps, nextState) {
// Добавляем ли мы новые элементы в список?
// Запомним положение прокрутки, чтобы мы позже могли ее отрегулировать.
if (this.props.list.length < nextProps.list.length) {
this.previousScrollOffset =
this.listRef.scrollHeight - this.listRef.scrollTop;
}
}
componentDidUpdate(prevProps, prevState) {
// Если установлено previousScrollOffset, значит мы только что добавили новые элементы.
// Отрегулируем прокрутку, чтобы новые элементы не вытесняли старые из виду.
if (this.previousScrollOffset !== null) {
this.listRef.scrollTop =
this.listRef.scrollHeight -
this.previousScrollOffset;
this.previousScrollOffset = null;
}
}
render() {
return (
<div ref={this.setListRef}>
{/* ...contents... */}
</div>
);
}
setListRef = ref => {
this.listRef = ref;
};
}
В приведенном выше примере componentWillUpdate
используется для чтения
свойства DOM. Однако при асинхронной отрисовке могут возникать задержки
между методами ЖЦ фазы отрисовки (render-фазы) (например, componentWillUpdate
и render
)
и методами ЖЦ фазы фиксации (commit-фазы) (например, componentDidUpdate
). Если в это
время пользователь производит действия, наподобие изменения размера окна,
значение scrollHeight
, считанное в componentWillUpdate
, будет устаревшим.
Два метода ЖЦ могут использоваться вместе следующим образом:
class ScrollingList extends React.Component {
listRef = null;
getSnapshotBeforeUpdate(prevProps, prevState) {
// Добавляем ли мы новые элементы в список?
// Запомним положение прокрутки, чтобы мы позже могли ее отрегулировать.
if (prevProps.list.length < this.props.list.length) {
return (
this.listRef.scrollHeight - this.listRef.scrollTop
);
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// Если у нас есть значение snapshot, значит мы только что добавили новые элементы.
// Отрегулируем прокрутку, чтобы новые элементы не вытесняли старые из виду.
// (здесь snapshot это значение, возвращенное из getSnapshotBeforeUpdate)
if (snapshot !== null) {
this.listRef.scrollTop =
this.listRef.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.setListRef}>
{/* ...contents... */}
</div>
);
}
setListRef = ref => {
this.listRef = ref;
};
}
Внимание!
Если вы пишете компонент для общего использования, полифилreact-lifecycles-compat
позволяет использовать новый метод ЖЦ getSnapshotBeforeUpdate
в старых версиях React.
Подробнее о том, как его использовать будет показано ниже.
Несмотря на то, что в этом статье мы пытались охватить наиболее распространенные
случаи использования, мы признаем, что все же могли что-то пропустить.
Если вы используете componentWillMount
, componentWillUpdate
или componentWillReceiveProps
какими-либо способами, которые не охвачены этой статьей, и не уверены, как правильно мигрировать с
этих методов ЖЦ, пожалуйста, создайте новую проблему рядом с нашей документацией,
предоставив примеры кода и как можно попутной информации. Мы
обновим данную статью новыми альтернативными паттернами по мере их появления.
Разработчики проектов с открытым исходным кодом могут задаваться вопросом:
что означают эти изменения для компонентов общего пользования? Если вы реализуете
приведенные выше предложения, что произойдет с компонентами, которые зависят от
нового статического метода ЖЦ getDerivedStateFromProps
? Вам тоже придётся выпустить
новую major-версию проекта и отказаться от совместимости с React 16.2 и старше?
К счастью, нет!
Когда будет опубликован React 16.3, мы также опубликуем и новый пакет npm,
. Данный
пакет будет производить полизаполнение компонентов таким образом, что
новые методы ЖЦ getDerivedStateFromProps
и getSnapshotBeforeUpdate
также будут работать
и с более старыми версиями React (0.14.9+).
Чтобы использовать этот полифил, сначала добавьте его как зависимость в вашу библиотеку:
# Yarn
yarn add react-lifecycles-compat
# NPM
npm install react-lifecycles-compat --save
Затем обновите свои компоненты, чтобы использовать новые методы ЖЦ (как описано выше).
Наконец, используйте полифил для того, чтобы ваш компонент был обратно совместим со старыми версиями React:
import React from 'react';
import {polyfill} from 'react-lifecycles-compat';
class ExampleComponent extends React.Component {
static getDerivedStateFromProps(props, state) {
// Здесь размещается ваша логика обновления состояния...
}
}
// Произведите полизаполнение своего компонента
// для работы со старыми версиями React:
polyfill(ExampleComponent);
export default ExampleComponent;