Информация по асинхронной отрисовке компонентов

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.

Вы можете найти их сигнатуры здесь.

Далее мы рассмотрим примеры использования данных методов ЖЦ.


Примеры



Внимание!

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


Инициализация состояния

В данном примере показан компонент с вызовом 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, компонент-контейнер библиотеки должен обрабатывать это за вас. Для разработчиков приложений мы создали небольшую библиотеку, create-subscription, чтобы помочь с этим. Мы опубликуем ее вместе с 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, следует вручную управлять подписками с помощью тех же методов, что и create-subscription использует у себя под капотом (как показано здесь), а также таким образом, который наиболее оптимален для использования в данной библиотеке.


Обновление состояния 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, react-lifecycles-compat. Данный пакет будет производить полизаполнение компонентов таким образом, что новые методы ЖЦ 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;