我有一个使用Hooks的react组件,我在其中单击一个按钮以对Hacker News API进行API调用,并将结果推送到数组中。然后,我将“故事”的状态设置为充满故事的数组。
第二个功能由按钮触发,控制台记录“故事”的状态,console.log是返回每个故事标题的.map。所有这些都很好。
如果我尝试在组件的返回中使用.map,它将无法正常工作。如果我将“故事”的状态初始化为["test", "test1", "test2"]
,则.map可以工作,但是一旦按下按钮将状态设置为故事的数组,.map就会停止工作。没有错误消息,只是内容消失了。
在这里我导入React并设置初始状态,我使用Axios,Fetch和Wretch进行API调用,所有操作都具有相同的结果:
import React, { useState, useEffect } from 'react';
const axios = require('axios');
import wretch from "wretch"
function App () {
const [stories, setStories] = useState(["test", "test2", "test3"]);
这是我触发以击中API并设置状态的函数:
function call () {
let storiesArr = [];
fetch('http://hacker-news.firebaseio.com/v0/topstories.json')
.then ((res) => res.json())
.then ((data) => {
for (let i = 0; i < 20; i++) {
fetch(`http://hacker-news.firebaseio.com/v0/item/${data[i]}.json`)
.then((res) => res.json())
.then((eachStory) => {
storiesArr.push(eachStory);
})
}
})
这是我用来检查状态是否设置为我认为的状态并确保.map在“故事”状态下起作用的第二个函数。这确实对我有用:
function test () {
console.log(stories);
stories.map((each) => {
return <p>{each.title}</p>
})
}
这是我的回报,这里的.map可以在初始状态下使用,但是一旦将状态设置为新数组就不会使用:
return (
<>
<h1 onClick={ () => call() } className="click">Fire off API calls</h1>
<h1 onClick={ () => test() } className="click">Test state of stories/<br/>.map each title</h1>
<table className="table">
<thead>
<tr>
<td>Table</td>
</tr>
</thead>
<tbody>
{
stories.map((each, i) => {
return <tr key={i}>
<td>{each.title ? each.title : each}</td>
</tr>
})
}
</tbody>
</table>
</>
);
我无法弄清楚为什么.map首先起作用,不再在返回中起作用,而是在函数中起作用。...
我将非常感谢任何人的任何帮助。
答案 0 :(得分:1)
您的数据获取看起来有些混乱,您知道可以使用Promise.all而不是推送到数组并循环。
我添加了一个check,以查看是否在设置状态之前仍安装了该组件。
const isMounted = useIsMounted();
//other code, I imagine useEfffect
function call() {
fetch(
'http://hacker-news.firebaseio.com/v0/topstories.json'
)
.then(res => res.json())
.then(data =>
Promise.all(
data.map(item =>
fetch(
`http://hacker-news.firebaseio.com/v0/item/${item}.json`
).then(res => res.json())
)
)
)
.then(
result => isMounted.current && setStories(result)
);
}
另外:http://hacker-news.firebaseio.com/v0/topstories.json返回400多个项目,这将使您对每个项目提出400多个请求,我不认为黑客新闻会喜欢它,因此可以切片结果或分页。 / p>
答案 1 :(得分:1)
我认为这比setState问题更多是异步处理问题。这是一个方便的所有(简化)示例
import React, { useState } from "react";
import ReactDOM from "react-dom";
// gets list of article ids
const getStoryList = async () => {
const res = await fetch(
"https://hacker-news.firebaseio.com/v0/topstories.json"
);
return await res.json();
};
// iterates over article list and returns a promise.all
const getStories = (articles, quantity) => {
return Promise.all(
articles.slice(0, quantity).map(async article => {
const storyRes = await fetch(
`https://hacker-news.firebaseio.com/v0/item/${article}.json`
);
return await storyRes.json();
})
);
};
// maps your response data
const formatStories = stories =>
stories.map(({ by, id, url, title = "No Title" }) => ({
id,
title,
url,
by
}));
function App() {
const [stories, setStories] = useState([]);
const call = async () => {
// first get list of stories
const res = await getStoryList();
// then async request all of the individual articles
// and push them into a group of promises
const storiesArr = await getStories(res, 20);
// then set your state.
setStories(formatStories(storiesArr));
};
return (
<div className="App">
<button onClick={call} className="click">
Fire off API calls
</button>
<table className="table">
<thead>
<tr>
<td>Table</td>
</tr>
</thead>
<tbody>
{stories.map(story => {
return (
<tr key={story.id}>
<td>
<a href={story.url}>{story.title}</a> by {story.by}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);