Я новичок в 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% случаев) данные не приходят, и я пытаюсь доступ к нему.
попробуйте заменить this.setValues() на requestAnimationFrame(this.setValues)
Тоже не работает :(
Я так думаю, потому что вы вызываете this.fetch_data(); внутри рендеринга() это означает, что сначала у вас нет данных в вашем состоянии, а после того, как есть данные, вы обновляете свой компонент, поэтому вы снова и снова вызываете this.fetch_data();
взгляните на жизненный цикл компонента React: https://code.likeagirl.io/understanding-react-component-life-cycle-49bf4b8674de
другое дело, что нет необходимости писать while(), потому что вы используете асинхронное ожидание.
Что я могу сделать, чтобы вызывать функцию fetch только тогда, когда var "symbol" меняет свое значение? Спасибо за ответ
Причина этой проблемы в том, что 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>
);
}
}
Я изменил функцию рендеринга, как вы сказали, но она так же вылетает :(
можете ли вы включить копию ответа json, который возвращает ваш запрос fetch()
?
Я нашел решение:
Просто поместите оператор if перед возвратом в рендере, говоря: «Изменился ли символ? Если да, извлеките, иначе не делайте этого».
Что вы имели в виду под "Изменился ли символ"? Какой метод/решение вы использовали? У меня такая же проблема в моем проекте.
Вы можете удалить
while(!data);
. Ключевое словоawait
гарантирует, чтоdata
будет присвоено значение до вызоваsetState
.