Javascript 如何从以前获取的数据更新React状态?
我是React和JS新手,还在学习,我正在尝试创建一个网站来显示来自SWAPI的数据,特别是人员数据。如果我从人的第一个页面获取信息,那么有一个名为“next”的字段,该字段具有检索下一个页面的url,依此类推。我希望能够在循环中使用此字段来获取所有数据页。以下是第一页的示例:Javascript 如何从以前获取的数据更新React状态?,javascript,reactjs,fetch,state,Javascript,Reactjs,Fetch,State,我是React和JS新手,还在学习,我正在尝试创建一个网站来显示来自SWAPI的数据,特别是人员数据。如果我从人的第一个页面获取信息,那么有一个名为“next”的字段,该字段具有检索下一个页面的url,依此类推。我希望能够在循环中使用此字段来获取所有数据页。以下是第一页的示例: { "count": 82, "next": "http://swapi.dev/api/people/?page=2", "previous": null, "results": [
{
"count": 82,
"next": "http://swapi.dev/api/people/?page=2",
"previous": null,
"results": [
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue", ...
因此,一旦我获取了/people/
,那么我就想获取/people/?page=2
这是我到目前为止的相关代码
class App extends Component {
constructor() {
super()
this.state = {
people: [],
url: '',
searchfield: ''
}
}
// Find first page of 'people' then loop through each page adding 'data.results' to 'people' state array
componentDidMount() {
this.setState({ url: 'https://swapi.dev/api/people/' }, () => {
console.log('initial url is', this.state.url)
for (var i = 0; i <= 3; i++) {
console.log('next url is', this.state.url)
fetch(this.state.url)
.then(response => response.json())
.then(data => this.setState((prevstate) => {
return {people: prevstate.people.concat(data.results), url: data.next}
}))
.catch(error => {
console.log(error)
});
}
});
}
componentDidUpdate(prevProps,prevState) {
if(prevState.url !== this.state.url) {
console.log('* new url is', this.state.url)
}
}
我认为在返回字段中添加状态更改就足够了,但事实并非如此,因此我尝试添加componentDidUpdate函数来尝试触发状态更改,但我认为这没有帮助。我还很好奇,当还没有更新任何内容时,componentDidUpdate的日志是如何首先出现的
目前,所有这一切只是将相同的4个页面连接到people数组中(控制台对此表示不满)
所以我的问题是,如何使用以前获取的数据正确设置url状态
编辑:好的,我忘了在我的问题中添加一件事,那就是我计划将此获取设置为通用,以便它可以接受SWAPI上的任何类别,并使用“下一步”字段确定何时停止获取数据。我确实有一段类似于Yousaf答案的代码,我获取了第一页,然后使用计数循环遍历所有单独的页面,但这意味着72次获取!那时,在页面之间循环似乎是更好的选择。我现在有了更好的办法。fetch()。调用fetch
时,它会立即返回一个值,并安排稍后执行实际的提取。实际上,它并不像您在其他语言/框架中可能使用的那样,以内联方式完成工作
因此,您的for
循环从0开始,调用fetch
,该循环使用当前URL安排提取(但实际上不执行提取),转到1,使用相同的URL安排提取(请记住,由于上一次提取尚未实际执行,所以它没有更改),等等
退出for
循环和回调函数后,执行fetch
s。现在,在React中,每次更新状态时,组件都会重新渲染。这是相当昂贵的计算,所以React尽量少做。React所做的一个优化是将一行中的一组状态更改聚合为一个大的状态更改。您只能看到componentdiddupdate
中的一个日志,因为它实际上只更新了一次
有几种方法可以解决这个问题。我的建议是使用JavaScript的语法。将async
放在原始setState
回调中的箭头函数之前,并将wait
放在fetch
之前。在循环中提取数据的方法完全错误fetch
函数返回一个承诺,实际请求是异步发出的。您应该做的是将fetch
函数返回的所有承诺保存在一个数组中,然后使用该函数解析所有这些承诺
要获取所需数据,请执行以下步骤:
在componentDidMount
函数中,创建一个数组,该数组将保存fetch
函数返回的所有承诺。创建一个循环并将fetch
函数返回的所有承诺添加到先前创建的中
之后,调用Promise.all
函数并传递包含所有承诺的数组<代码>承诺。所有
将返回一个对象数组。然后,您需要对所有这些Response
对象调用函数,以获取API返回的实际数据
下面是组件didmount
函数的外观
componentDidMount() {
const requests = [];
for (let i = 1; i <= 3; i++) {
requests.push(fetch('https://swapi.dev/api/people/?page=' + i));
}
Promise.all(requests)
.then(res => Promise.all(res.map(r => r.json())))
.then(data => {
const people = [];
data.forEach(d => people.push(...d.results));
this.setState({ people });
})
.catch(err => console.log(err.message));
}
演示
下面是一个从Swapi获取前3页数据并显示所有人姓名的示例。我已将您的答复标记为答案,因为它有助于我更好地理解异步请求,但如果您阅读了我的问题编辑,我忘了添加我计划执行的操作。我现在有了一个想法,我可以使用您的方法,但使用“计数”字段来确定在处理承诺数组(我不知道您可以这么做!)之前需要将多少页面捆绑到承诺数组中,即计数/10,如果剩余部分不是零,则添加1,否则按原样使用。
componentDidMount() {
const requests = [];
for (let i = 1; i <= 3; i++) {
requests.push(fetch('https://swapi.dev/api/people/?page=' + i));
}
Promise.all(requests)
.then(res => Promise.all(res.map(r => r.json())))
.then(data => {
const people = [];
data.forEach(d => people.push(...d.results));
this.setState({ people });
})
.catch(err => console.log(err.message));
}
async componentDidMount() {
const requests = [];
for (let i = 1; i <= 3; i++) {
requests.push(fetch('https://swapi.dev/api/people/?page=' + i));
}
try {
const responseArr = await Promise.all(requests);
const data = await Promise.all(responseArr.map(r => r.json()));
const people = [];
data.forEach(d => people.push(...d.results));
this.setState({ people });
} catch(error) {
console.log(error.message);
}
}