簡體   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