Реагировать, передавая аргумент состояния функции

#reactjs

#reactjs

Вопрос:

В классе React я вызываю функцию, которая ожидает аргумент:

 renderColumnChart(chart) {
  console.log('rendering chart');  
  console.log(typeof chart);  // <<<<<<<< OK! I get "Object"
  console.log(chart);  // <<<<<<<<<<<<<<< OK! I get the object in the console
  console.log(chart.title);  // <<<<<<<<< NO! I get the error :(
  return (
    <React.Fragment>
      <br />
      <Chart
        width={'100%'}
        height={'400px'}
        chartType="Bar"
        loader={<div>Loading Chart...</div>}
        data={[
          ['City', '2010 Population', '2000 Population'],
          ['New York City, NY', 8175000, 8008000],
          ['Los Angeles, CA', 3792000, 3694000],
          ['Chicago, IL', 2695000, 2896000],
          ['Houston, TX', 2099000, 1953000],
          ['Philadelphia, PA', 1526000, 1517000],
        ]}
        options={{
          chart: {
            title: 'Chart Title',
            subtitle: 'Hello World',
          },
          legend: { position: 'none' },
          chartArea: { width: '50%' },
          hAxis: {
            title: 'Total Population',
            minValue: 0,
          },
          vAxis: {
            title: 'City',
          },
        }}
      />
    </React.Fragment>
  );
}
  

в моем классе render() function я вызываю функцию условно, ожидая вызова API

 {this.state.isloading ? (
  <div>
    <h1>Loading...</h1>
  </div>
) : (
  this.renderColumnChart(this.state.reports[0])
)}
  

установив isLoading значение false после вызова API:

 componentDidMount() {
  this.getData();
  this.setState({
    isLoading: false,
  });
}
  

Но я получаю эту ошибку:
Uncaught TypeError: Cannot read property 'title' of undefined

Что вызывает недоумение, потому что: почему я могу регистрировать объект, но не какие-либо его значения?

Комментарии:

1. Является getData() ли это асинхронным?

2. getData является асинхронным, и так оно и есть setState . Поэтому он не будет ждать getData завершения, прежде чем установить isLoading значение false. Это означает, что он отобразит диаграмму до того, как данные будут возвращены из API. Вот почему вы получаете неопределенную ошибку. С точки зрения того, почему объект кажется определенным, я считаю, что это просто причуда консоли браузера. Даже если вы получаете эту ошибку, в фоновом режиме вызов API все равно выполняется и обновляет состояние. В консоли это означает, что ранее напечатанные значения обновляются

3. Кроме того, у вас есть опечатка, this.state.isloading но затем в setState вызове вы используете верблюжий регистр : isLoading .

4. Самым простым решением было бы установить состояние после возврата вызова API, и вы установили данные отчетов в состояние

5. @Jayce444 Ты нашел это!! Большое спасибо… это истощало!

Ответ №1:

Важно знать, что консоль Chrome в инструментах разработчика показывает «живой» (т. Е. Текущий На момент расширения) вид объекта.

Попробуйте следующий эксперимент. Скопируйте и вставьте следующий код в консоль в Chrome:

 const obj = {};
console.log(obj);
obj.foo = 'bar';
setTimeout(() => obj.bar = 'baz');
  

Первоначально он будет отображаться как ▶ {} , но если вы развернете его, он покажет, что у него есть два свойства, foo и bar , оба из которых были добавлены после регистрации объекта.

Единственный надежный способ регистрации объекта в том состоянии, в котором он находится при регистрации, — это создать его (глубокую) копию. Один из способов сделать это — использовать JSON.parse(JSON.stringify(obj)) , предполагая, что весь объект сериализуем в JSON .

Что касается вашего вопроса, есть одна возможность, если ваш код точно такой, как в вашем вопросе, который заключается в том, что у вас есть опечатка в вашей ссылке в render() функции на this.state.isloading то, когда это должно быть this.state.isLoading .

Но я подозреваю, что это не настоящая проблема. Я подозреваю, что то, что происходит, происходит во время первоначального рендеринга, значение this.state.reports[0] равно undefined , если вы инициализируете this.state = { reports: [], ...etc... } . Затем, после getData() асинхронного завершения, вы обновляете состояние, таким this.state.reports = [{ title: 'Foo', ...etc... }] образом, эффективно. Когда render() React снова вызывается React, он может видеть полный объект.

Наконец, как говорили другие, поскольку getData() это асинхронно, вам нужно переместить this.setState({ isLoading: false }) внутреннюю часть вашей getData() функции, когда или после reports того, как состояние установлено. В качестве альтернативы, если вы можете использовать async / await конструкции, сделайте getData() async, а затем componentDidMount() становится:

   async componentDidMount() {
    await this.getData();
    this.setState({ isLoading: false });
  }
  

Ответ №2:

добавьте isLoading: false, inside getData после получения ответа от вызова API, удалите его из componentDidMount

 componentDidMount() {
  this.getData();
  //this.setState({
  //  isLoading: false,
  //});
}
  

Комментарии:

1. Извините, я свой код не прокомментировал. Я обновлю приведенный выше код. @Dave Newton это асинхронная функция.

2. Я попробовал ваше предложение, и все равно оно не работает. Интересно, что даже если я установлю isLoading для состояния значение false и никогда не установлю для него значение true, я не вижу «Загрузка …» <h1> в браузере. Озадачивает.