[英]ReactJs re-render on state change
I am trying to do a sequence of fetching some API data and manipulating it as follows: 我正在尝试执行以下操作以获取一些API数据并按如下方式进行操作:
I am having some trouble with getting the timing with all these asynchronous activities right (I am pretty new to Javascript). 我在正确安排所有这些异步活动的时间方面遇到了一些麻烦(对于Javascript我是很陌生的)。
Here's what I have so far: 这是我到目前为止的内容:
class Map extends Component {
constructor(props) {
super(props);
this.state = {
addresses = [],
geocodedAddresses = []
}
}
componentDidMount() {
const geocodedAddresses = []
fetch('.....')
.then(result => result.json())
.then(addresses => this.setState({ addresses }, function() {
this.state.addresses.forEach(address => {
Geocode.fromAddress(address).then(geocodedAddress => {
geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
})
})
}))
console.log(geocodedAddresses) //Correctly contains the geocoded addresses
this.setState({ geocodedAddresses })
}
}
render() {
this.state.addresses.map(a => console.log(a)) //Evaluates correctly
this.state.geocodedAddresses.map(ga => console.log(ga)) //Yields nothing....
.....
}
}
So I don't quite understand why React does not re render when I do this.setState({ geocodedAddresses })
- Shouldn't react re render when I do setState? 所以我不太明白为什么我这样做时React不会重新渲染
this.setState({ geocodedAddresses })
-当我执行setState时不应该重新渲染吗?
There are a couple errors with your code. 您的代码有几个错误。 In the first place, the state object is being created with equals instead of colons:
首先,使用equals而不是冒号创建状态对象:
this.state = {
addresses: [],
geocodedAddresses: []
}
In the second place, you should take into account that your code is asynchronous. 其次,您应考虑到代码是异步的。 Only when the promises generated by the call to
Geocode.fromAddress
resolve you have the data for your geocodedAddresses
. 只有当通过调用产生的诺言
Geocode.fromAddress
解决您为您的数据geocodedAddresses
。
In the componentDidMount
, you are console logging the geocodedAdresses
and you report that you are seeing the right values. 在
componentDidMount
,您正在控制台记录geocodedAdresses
并报告看到了正确的值。 This is only because the log is update after the promises resolve. 这仅是因为在承诺解决之后,日志已更新。 But when you do the
console.log
the value at that moment for geocodedAdresses
is an empty array. 但是,当你做
console.log
在那一刻的价值geocodedAdresses
是一个空数组。 And that is the value that is being inserted in the component state
. 这就是在组件
state
插入的值。
In order to set the correct value for the state, you should call setState
when all your Geocode.fromAddress
promises have resolved. 为了为状态设置正确的值,您应该在所有
Geocode.fromAddress
承诺均已解决后调用setState
。 In order to do that you can use Promise.all method. 为此,您可以使用Promise.all方法。
So, your code would look like: 因此,您的代码如下所示:
class Map extends Component {
constructor(props) {
super(props);
this.state = {
addresses: [],
geocodedAddresses: []
}
}
componentDidMount() {
fetch('.....')
.then(result => result.json())
.then(addresses => {
Promise.all(addresses.map(address => Geocode.fromAddress(address)))
.then(geocodedAddresses => {
this.setState({
addresses,
geocodedAddresses
})
});
}))
}
}
render() {
this.state.addresses.map(a => console.log(a)) //Evaluates correctly
this.state.geocodedAddresses.map(ga => console.log(ga)) //Yields nothing....
.....
}
}
Note that with this solution setState
is only being called once. 请注意,使用此解决方案,
setState
仅被调用一次。
Since all your state
refers to addresses information, it could make sense to merge that information into a single key in the state
. 由于您所有的
state
涉及到地址信息,因此有可能将该信息合并到state
的单个键中。 You could initialize your state
in the constructor as: 您可以在构造函数中将
state
初始化为:
this.state = {
addresses: []
};
And then, you could populate the state
once all the promises resolve: 然后,一旦所有承诺都解决,您就可以填充
state
:
componentDidMount() {
fetch('.....')
.then(result => result.json())
.then(addresses => {
Promise.all(addresses.map(address => Geocode.fromAddress(address)))
.then(geocodedAddresses => {
const addressesState = addresses.map((address, i) => {
return {
address,
geocodedAddress: geocodedAddresses[i]
};
});
this.setState({ addresses: addressesState })
});
}))
}
}
You're right it's the async is slightly off. 您说对了,这是异步稍微关闭了。 The
console.log
will populate after it "appears" in the console, but when setState
has been called (and also when console.log
has been called) its value is still []
. console.log
将在它“出现”在控制台中后填充,但是当setState
被调用时(以及console.log
被调用时),它的值仍然是[]
。
if you're using async/await
you can wait for the fetch
chain to completely finish, or put the setState
within the then
. 如果您使用的是
async/await
,则可以等待fetch
链完全完成,或者将setState
放在then
。 With the setState
callback, it's probably better to do the latter. 使用
setState
回调,最好执行后者。
componentDidMount() {
const geocodedAddresses = []
fetch('.....')
.then(result => result.json())
.then(addresses => this.setState({ addresses }, function() {
this.state.addresses.forEach(address => {
Geocode.fromAddress(address).then(geocodedAddress => {
geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
})
})
console.log(geocodedAddresses) //Correctly contains the geocoded addresses
this.setState({ geocodedAddresses })
}))
}}
or 要么
componentDidMount = async () => {
const geocodedAddresses = []
let addresses = await fetch('.....').then(result => result.json())
addresses.forEach(address => { // assuming this is sync
Geocode.fromAddress(address).then(geocodedAddress => {
geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
})
})
this.setState({ geocodedAddresses,addresses })
}}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.