«Невозможно прочитать свойство undefined» в React после выборки и ожидания

Я новичок в React, и у меня проблема с получением:

Я делаю проект в реакции, где мне нужно получить пять разных URL-адресов в зависимости от того, какую компанию выбирает пользователь. Я много раз менял код, но он не работает.

Я пробовал с componentDidMount (но он вызывается только один раз, и я хочу получить много URL-адресов), с «пока (! this.state)», чтобы занять ожидание выборки, чтобы получить данные, прежде чем назначать их и т. д.

Функция извлечения:

async fetch_data() {
    var sel = this.props.select;
    symbol = companies[sel];
    url = 'https://www.alphavantage.co/query?function=' + func + '&symbol=' + symbol + '&outputsize=' + osize + '&apikey=' + api_key;
    var response = await fetch(url);
    var data = await response.json();

    while(!data);

    this.setState({data: data, loading: false });
    this.setValues();    
  }

Функция установки значений (обрезана, чтобы не было так долго):

setValues() {
    day = this.state.data["Meta Data"]["3. Last Refreshed"];
    open = this.state.data["Time Series (Daily)"][day]["1. open"];
    higher = this.state.data["Time Series (Daily)"][day]["2. high"];
  }

Функция рендеринга (тоже урезанная):

render() {

    this.fetch_data();
    if (this.state.loading || !this.state || !this.state.data) {
      return <div>Loading :D...</div>
    } else {
      return (
        <div>
          <h2>Symbol: {symbol}</h2>
          <p>Url: {url}</p>
          <ul>
            <li>
              Open price: {open}
            </li>
            <li>
              Higher price: {higher}
            </li>
          </ul>
        </div>
      );
    }
  }

На самом деле я сделал ту часть, когда пользователь выбирает компанию, а символ (и URL-адрес) меняется... Но проблема в том, что когда программа хочет получить доступ, например, к this.state.data["Time Series (Daily)"][day]["1. open"];, она падает, потому что она извлекает и получает данные.

Ошибка говорит:

Unhandled Rejection (TypeError): Cannot read property '3. Last Refreshed' of undefined:

  42 | setValues() {
> 43 |   day = this.state.data["Meta Data"]["3. Last Refreshed"];
     | ^  44 |   open = this.state.data["Time Series (Daily)"][day]["1. open"];
  45 |   higher = this.state.data["Time Series (Daily)"][day]["2. high"];

Приложение не всегда дает сбой и показывает результаты, но компонент рендеринга продолжает вызывать this.fetch_data() и происходит сбой, потому что иногда (например, в 80% случаев) данные не приходят, и я пытаюсь доступ к нему.

Вы можете удалить while(!data);. Ключевое слово await гарантирует, что data будет присвоено значение до вызова setState.

Tholle 07.04.2019 21:58

попробуйте заменить this.setValues() на requestAnimationFrame(this.setValues)

duc mai 07.04.2019 22:52

Тоже не работает :(

Octaviotastico 07.04.2019 22:57
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
3
2 044
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я так думаю, потому что вы вызываете this.fetch_data(); внутри рендеринга() это означает, что сначала у вас нет данных в вашем состоянии, а после того, как есть данные, вы обновляете свой компонент, поэтому вы снова и снова вызываете this.fetch_data();

взгляните на жизненный цикл компонента React: https://code.likeagirl.io/understanding-react-component-life-cycle-49bf4b8674de

другое дело, что нет необходимости писать while(), потому что вы используете асинхронное ожидание.

Что я могу сделать, чтобы вызывать функцию fetch только тогда, когда var "symbol" меняет свое значение? Спасибо за ответ

Octaviotastico 07.04.2019 22:35

Причина этой проблемы в том, что setState() асинхронный. В вашем случае это означает, что изменения состояния сделаны здесь:

this.setState({data: data, loading: false });

Не видны сразу при последующем вызове this.setValues(); (который сам по себе зависит от объекта data, определяемого в состоянии компонента). Решением для этих ситуаций является использование второго аргумента обратного вызова в методе setState() для запуска логики, которая зависит от соответствующего изменения состояния:

/* The state change will be observable when setValues() is called via
the callback */
this.setState({data: data, loading: false }, () => {

    this.setValues();    
});

В качестве предложения для реализации вашего компонента рассмотрите возможность пересмотра метода render(), чтобы day, open и higher обращались и отображались непосредственно в render(), а не кэшировали эти значения в другом месте, как вы, кажется, делаете. Итак, попробуйте что-то вроде этого:

render() {

    this.fetch_data();
    if (this.state.loading || !this.state || !this.state.data) {
      return <div>Loading :D...</div>
    } else {

      /* Call to setValues() no longer needed */

      const day = this.state.data["Meta Data"]["3. Last Refreshed"];
      const open = this.state.data["Time Series (Daily)"][day]["1. open"];
      const higher = this.state.data["Time Series (Daily)"][day]["2. high"];

      return (
        <div>
          <h2>Symbol: {symbol}</h2>
          <p>Url: {url}</p>
          <ul>
            <li>
              Open price: {open}
            </li>
            <li>
              Higher price: {higher}
            </li>
          </ul>
        </div>
      );
    }
  }

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

Octaviotastico 07.04.2019 22:32

можете ли вы включить копию ответа json, который возвращает ваш запрос fetch()?

Dacre Denny 07.04.2019 22:39
Ответ принят как подходящий

Я нашел решение:

Просто поместите оператор if перед возвратом в рендере, говоря: «Изменился ли символ? Если да, извлеките, иначе не делайте этого».

Что вы имели в виду под "Изменился ли символ"? Какой метод/решение вы использовали? У меня такая же проблема в моем проекте.

plkpiotr 22.08.2019 00:13

Другие вопросы по теме