[英]ReactJS: I can not get the state to update with API data when the asynchronous API data fetch is completed
I am having a bit of an issue rendering components before the state is set to the data from a returned asynchronous API request. 在将状态设置为来自返回的异步API请求的数据之前,渲染组件时遇到了一些问题。 I have a
fetch()
method that fires off, returns data from an API, and then sets the state to this data. 我有一个
fetch()
方法,该方法会触发,从API返回数据,然后将状态设置为该数据。 Here is that block of code that handles this: 这是处理此问题的代码块:
class App extends Component {
constructor() {
super();
this.state = {
currentPrice: null,
};
}
componentDidMount() {
const getCurrentPrice = () => {
const url = 'https://api.coindesk.com/v1/bpi/currentprice.json';
fetch(url).then(data => data.json())
.then(currentPrice => {
this.setState = ({
currentPrice: currentPrice.bpi.USD.rate
})
console.log('API CALL', currentPrice.bpi.USD.rate);
}).catch((error) => {
console.log(error);
})
}
getCurrentPrice();
}
You will notice the console.log('API CALL', currentPrice.bpi.USD.rate
) that I use to check if the API data is being returned, and it absolutely is. 您会注意到我用来检查API数据是否正在返回的
console.log('API CALL', currentPrice.bpi.USD.rate
),并且绝对是。 currentPrice.bpi.USD.rate
returns an integer ( 2345.55
for example) right in the console as expected. currentPrice.bpi.USD.rate
在控制台中按预期返回一个整数(例如2345.55
)。
Great, so then I assumed that this.setState = ({ currentPrice: currentPrice.bpi.USD.rate })
should set the state without an issue, since this data was received back successfully. 太好了,因此我假设
this.setState = ({ currentPrice: currentPrice.bpi.USD.rate })
可以正确设置状态,因为已成功接收到此数据。
So I now render the components like so: 现在,我将组件渲染如下:
render() {
return (
<div>
<NavigationBar />
<PriceOverview data={this.state.currentPrice}/>
</div>
);
}
}
export default App;
With this, I was expecting to be able to access this data in my PriceOverview.js
component like so: this.props.data
有了这个,我期望能够像下面这样在
PriceOverview.js
组件中访问此数据: this.props.data
I have used console.log()
to check this.props.data
inside my PriceOverview.js
component, and I am getting 'null' back as that is the default I set intially. 我已经使用
console.log()
来检查this.props.data
组件中的PriceOverview.js
,并且我得到了“ null”,因为这是我PriceOverview.js
设置的默认值。 The issue I am having is that the components render before the API fetch has ran it's course and updated the state with the returned data. 我遇到的问题是,组件在API提取运行之前就渲染了,并使用返回的数据更新了状态。 So when
App.js
renders the PriceOverview.js
component, it only passes currentPrice: null
to it, because the asynchronous fetch()
has not returned the data prior to rendering. 因此,当
App.js
渲染PriceOverview.js
组件时,它仅将currentPrice: null
传递给它,因为异步fetch()
尚未在渲染之前返回数据。
My confusion lies with this.setState
. 我的困惑在于
this.setState
。 I have read that React will call render any time this.setState
is called. 我已经读过,
this.setState
被调用,React就会调用render。 So in my mind, once the fetch()
request comes back, it calls this.setState
and changes the state to the returned data. 因此,在我看来,一旦
fetch()
请求返回,它将调用this.setState
并将状态更改为返回的数据。 This in turn should cause a re-render and the new state data should be available. 反过来,这将导致重新渲染,并且新的状态数据应该可用。 I would be lying if I didn't say I was confused here.
如果我不说我在这里感到困惑,那我会撒谎。 I was assuming that once the
fetch()
returned, it would update the state with the requested data, and then that would trigger a re-render. 我假设一旦
fetch()
返回,它将使用请求的数据更新状态,然后触发重新渲染。
There has to be something obvious that I am missing here, but my inexperience leaves me alone.. cold.. in the dark throws of despair. 这里肯定有我想念的东西,但是我的经验不足让我一个人..冷..在绝望的黑暗中。 I don't have an issue working with 'hard coded' data, as I can pass that around just fine without worry of when it returns.
我在使用“硬编码”数据时没有问题,因为我可以很好地传递数据而不必担心数据何时返回。 For example, if I set the state in App.js to
this.state = { currentPrice: [254.55] }
, then I can access it in PriceOverview.js
via this.props.data
with zero issue. 例如,如果我将App.js中的状态设置为
this.state = { currentPrice: [254.55] }
,则可以通过零问题通过this.props.data
在PriceOverview.js
访问它。 It's the async API request that is getting me here, and I am afraid it has gotten the best of me tonight. 是异步API请求将我带到了这里,恐怕今晚它已成为我的最佳选择。
Here App.js in full: 此处完整显示了App.js:
import React, { Component } from 'react';
import './components/css/App.css';
import NavigationBar from './components/NavigationBar';
import PriceOverview from './components/PriceOverview';
class App extends Component {
constructor() {
super();
this.state = {
currentPrice: null,
};
}
componentDidMount() {
const getCurrentPrice = () => {
const url = 'https://api.coindesk.com/v1/bpi/currentprice.json';
fetch(url).then(data => data.json())
.then(currentPrice => {
this.setState = ({
currentPrice: currentPrice.bpi.USD.rate
})
console.log('API CALL', currentPrice.bpi);
}).catch((error) => {
console.log(error);
})
}
getCurrentPrice();
}
render() {
return (
<div>
<NavigationBar />
<PriceOverview data={this.state.currentPrice}/>
</div>
);
}
}
export default App;
Here is PriceOverview.js in full: 这是完整的PriceOverview.js:
import React, { Component } from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
class PriceOverview extends Component {
constructor(props) {
super(props);
this.state = {
currentPrice: this.props.data
}
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.state.currentPrice != null ? <div className="price">{this.state.currentPrice}</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}
export default PriceOverview;
Thank you in advance to any help, it's much appreciated. 预先感谢您的任何帮助,非常感谢。
The thing is constructor of any JS class is called only once. 问题是任何JS类的构造函数都只能调用一次。 It is the
render
method that is called whenever you call this.setState
. 每当您调用
this.setState
时,就会调用render
方法。
So basically you are setting currentPrice
to null
for once and all in constructor and then accessing it using state so it will always be null. 因此,基本上,您是在构造函数中一次将
currentPrice
设置为null
,然后使用state访问它,因此它将始终为null。
Better approch would be using props. 更好的方法是使用道具。
You can do something like this in your PriceOverview.js
. 您可以在
PriceOverview.js
执行类似的PriceOverview.js
。
import React, { Component } from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
class PriceOverview extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.props.data!= null ? <div className="price">{this.props.data}</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}
export default PriceOverview;
Or you can use react lifecycle method componentWillReceiveProps
to update the state of PriceOverview.js
或者,您可以使用
PriceOverview.js
生命周期方法componentWillReceiveProps
来更新PriceOverview.js
的状态
componentWillReceiveProps(nextProps) {
this.setState({
currentPrice:nextProps.data
});
}
render() {
return (
<div className="overviewBar">
<div className="currentPrice panel">
{ this.state.currentPrice != null ? <div className="price">{this.state.currentPrice }</div> : <div className="price">Loading...</div> }
</div>
</div>
)
}
}
Ok First thing, when you're writting code on React the components that hold state are the class base components so ... What I see here is that you're creating two class base components so when you pass down props from your app class component to your PriceOverview wich is another class base component you're essentially doing nothing... Because when your constructor on your PriceOverview get call you're creating a new state on that Component and the previous state ( that's is the one you want to pass down) is being overwritten and that's why you're seem null when you want to display it. 好的,第一件事,当您在React上编写代码时,保持状态的组件是类的基础组件,所以...我在这里看到的是您正在创建两个类的基础组件,因此当您从应用程序类传递props时到PriceOverview的组件是另一个基本类组件,您实际上什么都不做...因为当您在PriceOverview上的构造函数被调用时,您正在该Component和先前的状态上创建一个新状态(这就是您想要的状态)传递)被覆盖,这就是为什么当您要显示它时您似乎为空。 So it should work if you just change your PriveOverview component to a function base component ( or a dumb component).
因此,只要将PriveOverview组件更改为基于函数的组件(或哑组件),它就可以工作。 So this way when you pass down the state via props, you're displaying the correct state inside of your div.
这样,当您通过道具传递状态时,就可以在div中显示正确的状态。 This is how would look like.
这是什么样子。
import React from 'react';
import './css/PriceOverview.css';
import bitcoinLogo from './assets/bitcoin.svg';
const PriceOverview = (data) => {
return (
<div className="overviewBar">
<div className="currentPrice panel">
//Im calling data here because that's the name you gave it as ref
//No need to use 'this.props' you only use that to pass down props
{data != null ? <div className="price">
{data}</div> : <div className="price">Loading...</div>
}
</div>
</div>
)
}
}
export default PriceOverview;
Whenever you're writing new components start always with function base components if you component is just returning markup in it and you need to pass some data go to his parent component update it (making the api calls there or setting the state there) and pass down the props you want to render via ref. 每当您编写新组件时,如果您的组件只是在其中返回标记,则始终从函数基础组件开始,并且您需要将一些数据传递给其父组件以对其进行更新(在其中进行api调用或在其中设置状态)并传递按下要通过ref渲染的道具。 Read the React docs as much as you can, hope this explanation was useful (my apologies in advance if you don't understand quite well 'cause of my grammar I've to work on that)
尽可能多地阅读React文档,希望这个解释有用(如果您不太了解我的道歉,因为我的语法我必须努力解决)
this.setState ({
currentPrice: currentPrice.bpi.USD.rate
})
Do not put an =
in this.setState
不要在
this.setState
中this.setState
=
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.