javascript - 如何从之前获取的数据中更新 React 状态?
问题描述
我是 React 和 JS 的新手,仍在学习,我正在尝试创建一个网站来显示来自 SWAPI API 的数据,特别是人员数据。如果我获取人的第一页,则有一个名为“next”的字段,其中包含检索下一页的 url,依此类推。我希望能够在循环中使用该字段来获取所有数据页面。这是第一页的示例:
{
"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)
}
}
我现在有一个固定的循环i
,直到我可以让它正常工作,否则它会无限循环。我的问题是,当尝试使用下一页的地址更新 url 状态时,直到循环完成才会发生,这是上面日志的输出。
* new url is https://swapi.dev/api/people/
initial url is https://swapi.dev/api/people/
4 next url is https://swapi.dev/api/people/ < this happens 4 times
* new url is http://swapi.dev/api/people/?page=2
我认为在返回字段中添加状态更改就足够了,但事实并非如此,所以我尝试添加 componentDidUpdate 函数来尝试触发状态更改,但我认为它没有帮助。我也很好奇,当尚未更新任何内容时,来自 componentDidUpdate 的日志是如何首先出现的?
目前,所有这一切都是将相同的 4 页连接到 people 数组中(并且控制台抱怨它)。
所以我的问题是,如何使用以前获取的数据正确设置 url 状态?
编辑:好的,我忘记添加到我的问题中的一件事是,我打算使这个 fetch 通用,以便它可以接受 SWAPI 上的任何类别并使用“下一个”字段来确定何时停止获取数据。我确实有一段类似于 Yousafs 答案的前一段代码,我获取了第一页,然后使用计数循环遍历所有单独的页面,但这意味着 72 次获取!那时循环浏览页面似乎是一个更好的选择。我现在有一个更好的主意。
解决方案
fetch()
是异步的。当您调用 时fetch
,它会立即返回 aPromise
并安排稍后执行实际提取。它实际上并没有像您可能习惯于使用其他语言/框架那样内联工作。
因此,您的for
循环从 0 开始,调用fetch
调度与当前 URL 的 fetch(但实际上并不执行 fetch),转到 1,使用相同的 URL 安排 fetch(请记住,自上一次 fetch 以来它没有改变) '尚未实际执行)等。
一旦你的for
循环和回调函数退出,fetch
s 就会执行。现在,在 React 中,每次更新状态时,组件都会重新渲染。这计算起来非常昂贵,因此 React 尝试尽可能少地执行此操作。React 所做的优化之一是它使状态更改异步,以便一行中的一堆状态更改聚合成一个大的状态更改。您只会看到一个日志,componentDidUpdate
因为它实际上只更新了一次。
有几种方法可以解决这个问题。我的建议是使用 JavaScript 的async/await语法。放在async
原始setState
回调中的箭头函数await
之前,放在fetch
.