Как сделать статьи из источника spefi c и предотвратить слишком много запросов случайно? - PullRequest
0 голосов
/ 17 февраля 2020

Я пытаюсь создать приложение, которое отображает источники новостей в виде списка, и у каждого источника есть ссылка «Лучшие заголовки», которая переводит URL в конечную точку «/ заголовки». На этой конечной точке я хочу отобразить список верхних заголовков из указанного источника новостей c.


Приложение. 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;

Источники новостей отправляется в качестве реквизита в Sources-component для отображения списка источников. А также компоненту Headlines для получения доступа к source.id


Sources. 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;

В компоненте Заголовки я хочу отобразить заголовки из второго вызова и вернуть список верхних заголовков из указанного источника c.

Например, если я нажму ссылку "Верхние заголовки" внутри BB C Элемент списка новостей, тогда конечная точка "/ заголовки" должна показывать только верхние заголовки с BB C.


Проблема в том, что я не знаю, где разместить второй вызов для заголовков. Я попытался поместить его в Headlines-компонент, а затем использовать source.id, который становится опорой в новом вызове (помещая его в строку шаблона в качестве переменной), но это привело к бесконечной волне запросов http, которые замерзли Некоторое время в моем браузере и после того, как мне удалось остановить выполнение кода, API новостей заблокировал меня из-за тайм-аута (ошибка 429 для слишком большого числа запросов).


Идея в двух словах:

  • На первой странице показан список источников, каждый из элементов списка имеет ссылку на верхние заголовки.
  • После нажатия на ссылку, URL перемещается в «/ заголовки», где страница отображает заголовки для указанного c источника.

Может кто-нибудь сказать мне, если я пропускаю только маленький кусочек головоломки или мне нужно сделать основную переписать в иерархия рендеринга?

Естественно, я не буду здесь давать свой ключ API, поэтому, к сожалению, вы не сможете проверить это, если у вас нет ключа от News API.

GitHub repo


* 10 46 * РЕДАКТИРОВАТЬ: Теперь, когда я думаю об этом, могу ли я отправить source.id в качестве реквизита через компонент Link to Headlines?

Я видел что-то вроде этого:

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

Но как мне получить доступ к этому в компоненте Headline?

Так как я использую компоненты функций, this.props.location.source не будет работать.

Есть какой-то хук для этого или чего-то еще?


ОБНОВЛЕНИЕ: я добавил это в Headline-компонент:

const { source } = useParams();

Но теперь я получаю ошибку:

React Hook "useParams" вызывается условно. React Hooks должны вызываться в том же порядке в каждом компоненте рендера. Вы случайно позвонили в React Hook после раннего возвращения?

1 Ответ

0 голосов
/ 05 марта 2020

OK. Мне удалось решить это с помощью:

Источники. 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");
...