繁体   English   中英

如何从之前获取的数据中更新 React state?

[英]How do I update a React state from previously fetched data?

我是 React 和 JS 的新手,仍在学习,我正在尝试创建一个网站来显示来自 SWAPI API 的数据,特别是人员数据。 如果我获取第一页的人,有一个名为“下一页”的字段,其中包含 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 state 时,直到循环完成后才会发生,这是上面日志中的 Z78E6221F6393D1356681DB398F14CEZ6D。

* 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

我认为在返回字段中添加 state 更改就足够了,但还不够,所以我尝试添加 componentDidUpdate function 以尝试触发 state 更改,但我认为它没有帮助。 我也很好奇,当尚未更新任何内容时,来自 componentDidUpdate 的日志是如何首先出现的?

目前,所有这一切都是将相同的 4 页连接到 people 数组中(并且控制台抱怨它)。

所以我的问题是,如何使用先前提取的数据正确设置 url state?

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

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

因此,您的for循环从 0 开始,调用fetch安排使用当前 URL 进行提取(但实际上并不执行提取),转到 1,使用相同的 URL 安排提取(请记住,自上一个fetch 尚未实际执行)等。

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

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

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

要获取所需的数据,请执行以下步骤:

  1. componentDidMount function 中,创建一个数组来保存fetch function 返回的所有承诺。 创建一个循环并在您之前创建的中添加fetch function 返回的所有承诺。

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

这是您的componentDidMount function 的外观。

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 function 也可以使用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);
    }
}

演示

这是一个从 Swapi API 获取前 3 页数据并显示所有人姓名的演示

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM