首页 > 解决方案 > 如何从之前获取的数据中更新 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 次获取!那时循环浏览页面似乎是一个更好的选择。我现在有一个更好的主意。

标签: javascriptreactjsfetchstate

解决方案


fetch()异步的。当您调用 时fetch,它会立即返回 aPromise并安排稍后执行实际提取。它实际上并没有像您可能习惯于使用其他语言/框架那样内联工作。

因此,您的for循环从 0 开始,调用fetch调度当前 URL 的 fetch(但实际上并不执行 fetch),转到 1,使用相同的 URL 安排 fetch(请记住,自上一次 fetch 以来它没有改变) '尚未实际执行)等。

一旦你的for循环和回调函数退出,fetchs 就会执行。现在,在 React 中,每次更新状态时,组件都会重新渲染。这计算起来非常昂贵,因此 React 尝试尽可能少地执行此操作。React 所做的优化之一是它使状态更改异步,以便一行中的一堆状态更改聚合成一个大的状态更改。您只会看到一个日志,componentDidUpdate因为它实际上只更新了一次。

有几种方法可以解决这个问题。我的建议是使用 JavaScript 的async/await语法。放在async原始setState回调中的箭头函数await之前,放在fetch.


推荐阅读