[英]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 解決所有這些承諾。
要獲取所需的數據,請執行以下步驟:
在componentDidMount
function 中,創建一個數組來保存fetch
function 返回的所有承諾。 創建一個循環並在您之前創建的中添加fetch
function 返回的所有承諾。
之后,調用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.