如何从特定来源呈现文章并防止意外请求过多?



我正在尝试制作一个将新闻来源显示为列表的应用程序,并且每个来源都有一个"热门标题"链接,该链接将URL带到"/headlines"端点。在那个端点上,我想呈现来自该特定新闻来源的热门头条列表。

<小时 />

应用.js

import React, { useState, useEffect } from "react";
import Sources from "./components/Sources";
import Pagination from "./components/Pagination";
import Headlines from "./components/Headlines";
import axios from "axios";
import { Key } from "./API_Key"; // Import API key from outside the code.
import "./App.css";
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
const App = () => {
// Initialize states
const [sources, setSources] = useState([]);
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [sourcesPerPage] = useState(10);
useEffect(() => {
// Get news sources from News API
const fetchNews = async () => {
setLoading(true);
const res = await axios.get(
`http://newsapi.org/v2/sources?apiKey=${Key}`
);
setSources(res.data.sources); // Add news sources to "sources" state
setLoading(false);
console.log(res.data.sources.map(source => source.id));
};
fetchNews();
}, []);
const indexOfLastSource = currentPage * sourcesPerPage; // Get last source index
const indexOfFirstSource = indexOfLastSource - sourcesPerPage; // Get first source index
const currentSources = sources.slice(indexOfFirstSource, indexOfLastSource); // Get current sources
// Change page
const paginate = pageNumber => setCurrentPage(pageNumber);
return (
<Router>
<div className="container-fluid bg-dark mb-0">
<div className="container pt-3 pb-5">
<h3>
<Link to="/" className="badge badge-info">
Back to sources
</Link>
</h3>
<h1 className="text-primary mb-3">News from News API</h1>
<Switch>
<Route
exact
path="/"
render={props => (
<React.Fragment>
<Sources sources={currentSources} loading={loading} />
<Pagination
sourcesPerPage={sourcesPerPage}
totalSources={sources.length}
paginate={paginate}
/>
</React.Fragment>
)}
/>
<Route
path="/headlines"
render={props => (
<React.Fragment>
<Headlines sources={currentSources} loading={loading} />
<Pagination
sourcesPerPage={sourcesPerPage}
totalSources={sources.length}
paginate={paginate}
/>
</React.Fragment>
)}
/>
</Switch>
<h3 className="mb-0">
<a
href="https://newsapi.org/"
className="badge badge-info"
target="_blank"
rel="noopener noreferrer"
>
Powered by News API
</a>
</h3>
</div>
</div>
</Router>
);
};
export default App;

新闻源作为道具发送到源组件以呈现源列表。以及标题组件以访问source.id

<小时 />

来源.js

import React from "react";
import { Link } from "react-router-dom";
const Sources = ({ sources, loading }) => {
if (loading) {
return <h2>Loading...</h2>; // Show text "Loading..." if loading not done.
}
/* Map through the sources received as props, and return them as a list */
return (
<ul className="list-group mb-4">
{sources.map(source => (
<li key={source.id} className="list-group-item itemhover">
<h4>{source.name}</h4>
<p>{source.description}</p>
<Link to="/headlines" className="btn btn-success mr-5">
Top headlines
</Link>
<a href={source.url} target="_blank" rel="noopener noreferrer">
{source.url}
</a>
</li>
))}
</ul>
);
};
export default Sources;

新闻源数组被映射,组件为每个源返回一个列表项,其中包含指向顶部标题的链接。

<小时 />

头条新闻.js

import React from "react";
const Headlines = ({ headlines, loading }) => {
if (loading) {
return <h2>Loading...</h2>; // Show text "Loading..." if loading not done.
}
/* Map through the headlines received as props, and return them as a list */
return (
<ul className="list-group mb-4">
{headlines.map(headline => (
<li key={headline.id} className="list-group-item itemhover">
<h4>{headline.title}</h4>
<p>{headline.description}</p>
<a href={headline.url} target="_blank" rel="noopener noreferrer">
{headline.url}
</a>
</li>
))}
</ul>
);
};
export default Headlines;

在标题组件中,我想映射第二次调用的标题,并返回来自特定来源的热门标题列表。

例如,如果我点击BBC新闻列表项内的"热门头条"链接,那么"/headlines"端点应该只显示BBC的头条新闻。


问题是我不知道在哪里发出第二次头条新闻。我试图将其放在 Headlines-组件中,然后在新调用中使用作为道具提供的 source.id(将其作为变量放在模板字符串中(,但这导致了无休止的 http 请求浪潮,这冻结了我的浏览器一段时间,在我设法阻止代码运行之后, 新闻 API 因超时而阻止了我(请求过多出现错误 429(。


简而言之:

  • 第一页显示来源列表,每个列表项都有一个指向顶部标题的链接。
  • 点击链接后,网址将移至"/headlines",页面在其中呈现特定来源的热门标题。

谁能告诉我我是否只遗漏了一小部分拼图,或者我是否需要对渲染层次结构进行重大重写?

当然,我不会在这里提供我的 API 密钥,所以如果您没有来自新闻 API 的密钥,则无法遗憾地对其进行测试。

GitHub 存储库

>EDIT:现在我想到了,我可以通过链接到标题组件将source.id作为道具发送吗?

我见过这样的东西:

<Link to={{ pathname: '/headlines', source: { sourceID: source.id } }}>Top headlines</Link>

但是如何在标题组件中访问它呢?

由于我使用的是函数组件,因此 this.props.location.source将无法正常工作。

这个有什么钩子吗?

>更新: 我将其添加到标题组件中:
const { source } = useParams();

但是现在我得到一个错误:

React Hook "useParams" 是有条件地调用的。在每个组件渲染中,必须以完全相同的顺序调用 React Hooks。您是否在提前返回后不小心调用了 React Hook?

好的。我设法用这些解决了这个问题:

资料来源.js

<Link
to={{ pathname: `/headlines/?source=${source.id}`, source: { sourceID: source.id }}}
className="btn btn-success mr-5"
>
Top headlines
</Link>

头条新闻.js

// Get source.id (passed in Sources.js component) parameter from URL
const urlParams = new URLSearchParams(window.location.search);
const source = urlParams.get("source");

最新更新