我正在尝试按以下步骤提取一些API数据并对其进行处理:
我在正确安排所有这些异步活动的时间方面遇到了一些麻烦(我对Java语言还很陌生)。
这是我到目前为止所拥有的:
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....
.....
}
}
所以我不太明白为什么this.setState({ geocodedAddresses })
时React无法重新渲染-setState时不应该重新渲染吗?
答案 0 :(得分:1)
您的代码有几个错误。首先,使用等于而不是冒号创建状态对象:
this.state = {
addresses: [],
geocodedAddresses: []
}
第二,应该考虑代码是异步的。只有调用Geocode.fromAddress
所产生的承诺能够解决您的geocodedAddresses
的数据。
在componentDidMount
中,您正在控制台记录geocodedAdresses
,并报告看到了正确的值。这仅是因为在承诺解决之后,日志已更新。但是,当您执行console.log
时,geocodedAdresses
那时的值是一个空数组。这就是要插入组件state
中的值。
为了为状态设置正确的值,当您所有的setState
承诺都得到解决后,您应该致电Geocode.fromAddress
。为此,您可以使用Promise.all方法。
因此,您的代码如下:
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....
.....
}
}
请注意,使用此解决方案,setState
仅被调用一次。
由于您所有的state
都指向地址信息,因此可以将这些信息合并到state
中的单个键中。您可以在构造函数中将state
初始化为:
this.state = {
addresses: []
};
然后,一旦所有承诺都解决,您就可以填充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 })
});
}))
}
}
答案 1 :(得分:0)
您说对了,这是异步稍微关闭了。 console.log
会在之后填充,它会“出现”在控制台中,但是当调用setState
时(以及调用console.log
时),其值为仍然[]
。
如果您使用的是async/await
,则可以等待fetch
链完全完成,或将setState
放在then
中。使用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 })
}))
}}
或
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 })
}}