如何从以前获取的数据更新React状态?

时间:2020-06-15 16:25:31

标签: javascript reactjs fetch state

我是React和JS的新手,并且仍然在学习,我试图创建一个网站来显示SWAPI API中的数据,尤其是People数据。如果我获取人员的第一页,则存在一个名为“ 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

我认为在return字段中添加状态更改就足够了,但是还不够,所以我尝试添加componentDidUpdate函数来尝试触发状态更改,但是我认为这样做没有帮助。我还很好奇,当尚未进行任何更新时,如何首先显示来自componentDidUpdate的日志?

目前,所有这些操作都是将相同的4页连接到people数组中(控制台对此有所抱怨)。

所以我的问题是,如何使用上一次获取的数据正确设置url状态?

编辑:好的,我忘记添加到我的问题中的一件事是,我正计划使此访存泛型化,以便它可以接受SWAPI上的任何类别,并使用“下一个”字段来确定何时停止访存数据。我确实有一段类似于Yousafs答案的代码,我获取了第一页,然后使用计数循环遍历所有单独的页面,但这意味着要进行72次获取!那是浏览页面似乎更好的选择。我现在有个更好的主意。

2 个答案:

答案 0 :(得分:1)

fetch()asynchronous。当您调用fetch时,它会立即返回Promise并安排实际的获取以稍后执行。它实际上并没有像您以前使用其他语言/框架那样直接进行内联工作。

因此,您的for循环从0开始,调用fetch,该{<1>安排使用当前URL进行抓取(但实际上并未执行抓取),因此变为1计划使用相同的URL进行抓取(请记住,由于之前的抓取尚未实际执行,所以它没有更改),等等。

一旦您的for循环和回调函数退出,fetch便会执行。现在,在React中,每次更新状态时,组件都会重新渲染。这是非常昂贵的计算,因此React尝试尽可能少地执行它。 React进行的一种优化是{{​​3}},以便将一连串的状态更改汇总为一个大的状态更改。您只会从componentDidUpdate看到一个日志,因为它实际上只更新了一次。

有几种方法可以解决此问题。我的建议是利用JavaScript的makes state changes asynchronous语法。将async放在原始setState回调的箭头函数之前,并将await放在fetch之前。

答案 1 :(得分:1)

您在循环中获取数据的方法是完全错误的。 fetch函数返回一个Promise,并且实际请求是异步进行的。您应该做的是将fetch函数返回的所有promise保存在一个数组中,然后使用Promise.all()函数解析所有这些promise。

要获取所需的数据,请按照以下步骤操作:

  1. componentDidMount函数中,创建一个数组,该数组将保存fetch函数返回的所有promise。创建一个循环,并将fetch函数返回的所有promise添加到先前创建的中。

  2. 然后,调用Promise.all函数并传递包含所有诺言的数组。 Promise.all将返回Response个对象的数组。然后,您需要在所有这些Response对象上调用.json()函数,以获取API返回的实际数据。

这是您的componentDidMount函数的外观。

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));
}

上面编写的componentDidMount函数也可以使用async-await语法编写。

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);
    }
}

演示

这是一个demo,可从Swapi API提取前3页的数据并显示所有人的姓名。