我有一个使用 Hooks 的反应组件,我单击一个按钮对 Hacker News API 进行 API 调用并将结果推送到数组中。然后我把"故事"的状态设置为充满故事的数组。
我有一个按钮触发了第二个函数,该按钮控制台记录"故事"的状态.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 和 f 设置状态的函数:
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 一开始有效,在返回中不再工作,但在函数中确实有效......
我将非常感谢任何人可以提供的任何帮助。
你的数据获取看起来有点混乱,你知道你可以使用 Promise.all 而不是推送到数组和循环。
我添加了一个检查,以查看在设置状态之前组件是否仍在安装。
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 多个请求,我认为黑客新闻不会欣赏这一点,所以可能会切片结果并/或分页它。
我认为这更像是一个异步处理问题,而不是一个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);