Передача извлеченных данных в качестве реквизита (JSON) в состояние для рендеринга данных

В настоящее время я получаю данные из API в формате JSON при запуске своей саги. Процесс загрузки начинается, когда компонент смонтировался. Это означает, что компонент рендерится два раза.

Теперь, когда данные доступны в виде реквизита. Я могу использовать его для рендеринга.

Мой подход к этому выглядит следующим образом: у меня есть:

  1. Конструктор с начальным состоянием
  2. Я извлекаю данные в "componentDidMount"
  3. У меня есть функция, которая берет свойства JSON из реквизита и помещает их в новые переменные.
  4. Я запускаю эту функцию в моей функции render(), когда реквизиты содержат полученные данные.

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

Мои вопросы:

  1. Как предотвратить зацикливание, когда render() запускается один раз?
  2. Как я могу спроектировать это так, чтобы определенные свойства извлеченного объекта сливались в новый объект и как

Я надеюсь, что описал самые важные вещи о моей проблеме. Вот код:

class Dashboard extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        deviceInfo: {
            name: "Initial Name",
            batLevel: "78%",
        }
    }
}

componentDidMount() {
    this.props.requestApiData();
}

updateDeviceInfoWithState (){
    const devices = (this.props.data.data);

    if(devices){
        const newDeviceInfo = this.state.deviceInfo;
        newDeviceInfo.name = devices[0].shadow.desired.payload.refAppData.name;
        newDeviceInfo.batLevel = devices[0].shadow.reported.payload.refAppData.batteryState.level;
        this.setState({
            deviceInfo: newDeviceInfo,
        });
    }
}

render() {
    this.updateDeviceInfoWithState()

    return (
        <div className='container'>
              <p> {this.state.deviceInfo.name} </p>
              <p> {this.state.deviceInfo.batLevel} </p>
        </div>
    )
}...

person Aleks    schedule 21.07.2018    source источник


Ответы (2)


Обновление состояния в методе рендеринга не рекомендуется, так как это может вызвать бесконечный цикл.

В вашем случае состояние избыточно, так как вы берете данные только из реквизита или заменяете его значениями по умолчанию. Вместо использования состояния верните name и batLevel в методе updateDeviceInfoWithState и используйте его в методе render.

Пример (не проверено):

class Dashboard extends React.Component {
  componentDidMount() {
      this.props.requestApiData();
  }

  updateDeviceInfoWithState (){
      const devices = this.props.data.data;

      if(devices){
        const device = devices[0].shadow;

        return {
          name: device.desired.payload.refAppData.name,
          batLevel: device.reported.payload.refAppData.batteryState.level
        };
      }

      return  {
        name: "Initial Name",
        batLevel: "78%",
      };
  }

  render() {
      const { name, batLevel } = this.updateDeviceInfoWithState();

      return (
          <div className='container'>
                <p> {name} </p>
                <p> {batLevel} </p>
          </div>
      );
}...

Примечание 1. Если вы хотите отделить свой компонент от состояния, лучше использовать простые свойства в качестве входных данных для данных. Например, этому компоненту нужны свойства name и batLevel. Ему не нужно знать о массиве устройств, тени, полезной нагрузке и т. д. Вы можете подготовить данные, когда получите их в саге, или использовать селектор редукции в mapStateToProps.

Примечание 2. Если вам действительно нужны данные в вашем состоянии, вы можете использовать ссылку getDerivedStateFromProps (React 16.3) или обновить состояние в componentWillReceiveProps, если вы используете более старую версию.

person Ori Drori    schedule 21.07.2018
comment
Это работает! Требовалась вторая правка. Мне пришлось вернуть {name} и {batLevel} вместо {this.state.deviceInfo.name}. Благодарю вас! - person Aleks; 22.07.2018
comment
Не за что :) Меня вдруг осенило, что я забыл изменить фактическое использование состояния в методе рендеринга. - person Ori Drori; 22.07.2018
comment
Как можно поместить эти два свойства в новый объект? Например, я хочу использовать таблицу для отображения данных устройств, поэтому мне нужен объект, который нужно передать в таблицу. - person Aleks; 22.07.2018
comment
Верните все, что вы хотите, из метода updateDeviceInfoWithState (объект с двумя свойствами) или просто повторите массив devices в рендеринге и создайте элементы напрямую. Вы можете упростить себе жизнь, отформатировав данные в саге или в селекторе редукса, чтобы вы просто визуализировали данные в компоненте. - person Ori Drori; 22.07.2018

В этом случае вы можете использовать метод ComponentWillRecieveProps, подобный этому

componentWillRecieveProps(nextProps) {
// Condition as per ur requirement.
If(this.props.data != nextProps.data) {
    this.updateDeviceInfoWithState(nextProps)
}

}

Этот метод будет работать только при изменении реквизитов вашего компонента.

person Gurpreet Singh    schedule 21.07.2018
comment
Примечание componentWillRecieveProps устарело. reactjs.org/docs/ - person wdm; 22.07.2018