マルチページAPI呼び出しを扱うこと


今日、私たちは連続したAPI呼び出しをデータの複数のページをフェッチする方法の例を歩いています.私たちの目標は、小さなWebアプリケーションを使用して反応を使用してhooksapiは、動的にユーザーパラメータに基づいてロードされます.
我々は、複数のゲーム・ベンダー間で販売のためのデータを提供する、フリーAPI CheapShark APIからデータを取得する.CheapSharkはページ単位でデータを返すので、結果の1ページ以上が返されると複数のAPI呼び出しが結果を取得する必要があります.
完了したデモプロジェクトhereと完成コードhereを確認できます.

アプリ概要
私たちのデモアプリケーションは、ユーザーがゲームの取引を検索するための3つのパラメータを取ることができます.あなたは、Chapapshark API docsでパラメタを取ることができるのを見ることができます.我々は、ユーザーのアクションを処理するためにデータとリーチフックAPIを取得するAxiosを使用します.
Cheapshark APIがここで例の呼び出しで返すものの例を試すことができます.
APIは、それが15ドルの下で価格で見つけることができるすべての取引を返します、しかし、ページ番号パラメタと5の最大ページサイズなしで、それは結果の1ページを返します.以下のページを通してすべての結果を得る方法を見ます.
https://www.cheapshark.com/api/1.0/deals?storeID=1&upperPrice=15&pageSize=5
初期プロジェクト設定
ので、基本的な作成反応アプリBoilerPlateから始めて、 パッケージをインストールしましょう.詳細については、作成する反応アプリaxiosをチェックしてください.
npx create-react-app steam-sales-pagination;
cd steam-sales-pagination;
npm i axios;
npm start;
まず、usefetchgamesと呼ばれるカスタムフックファイルを作成します.Cheapsharkからデータをフェッチするのを扱うヘルパーディレクトリのjs.このカスタムフックは、ユーザー入力の検索パラメータと結果のページ数を取るために取る必要がありますので、我々は小道具として宣言する必要があります.
APIを定数変数と呼ぶために、我々のベースURLを宣言しましょう.私たちは、APIの呼び出しを使用して、ユーザーのアクションを処理し、データを取得するためのUseEffectとUsereducerフックを作るので、先に行くと、それらをインポートしてください.
// useFetchGames.js

...

import { useReducer, useEffect } from "react";
import axios from "axios";

const BASE_URL =
  "https://cors-anywhere.herokuapp.com/https://www.cheapshark.com/api/1.0/deals?storeID=1&pageSize=5";

export default function useFetchGames(params, page) {
  return <div></div>;
}

...

docs
還元剤の開発
我々のusefetchgamesフックに戻って、我々の縮小をつくることができます.まず、我々の行動を定義する必要があります.リクエストを作成するためのアクションを作成し、データ、エラーメッセージ、および次のページを取得します.
// useFetchHooks.js

...

const ACTIONS = {
  MAKE_REQUEST: "make-request",
  GET_DATA: "get-data",
  ERROR: "error",
  NEXT_PAGE: "next-page",
};

...

私たちの還元器では、我々の行動を処理するためにswitch文を作成します.MakeMountリクエストアクションでは、新しい変数を持つ新しいリクエストが作成されるたびに、私たちの読み込み変数をtrueにロードし、ゲームの配列を空にするよう設定します.GetRuleデータアクションでは、状態を返し、読み込み状態をfalseに戻し、アクションペイロードからゲームの配列を設定します.エラーの場合、ゲームの配列が空に設定され、エラー変数がペイロードエラーに設定されます.
セットする我々の最終的な行動は、NextCardページです.我々は、我々の第2のAPI呼び出しで結果のもう一つのページをチェックした後に、この行動を派遣しています.APIコールを書くとき、以下のペイロードを定義します.
// useFetchHooks.js

...

function reducer(state, action) {
  switch (action.type) {
    case ACTIONS.MAKE_REQUEST:
      return { loading: true, games: [] };
    case ACTIONS.GET_DATA:
      return { ...state, loading: false, games: action.payload.games };
    case ACTIONS.ERROR:
      return {
        ...state,
        loading: false,
        error: action.payload.error,
        games: [],
      };
    case ACTIONS.NEXT_PAGE:
      return { ...state, hasNextPage: action.payload.hasNextPage };
    default:
      return state;
  }
}

...

私たちの行動が定義されたので、我々のフックを書き終えましょう.まず最初に、我々の還元器と我々の初期の状態に反応からusereducerフックを通過する必要があります.usereducer私たちの状態と派遣機能を返します.我々は現在我々の州を返すために我々のフックをセットすることができます.
// useFetchHooks.js

...

export default function useFetchGames(params, page) {
  const [state, dispatch] = useReducer(reducer, { games: [], loading: true });
  return state;
}

...

我々は、我々のパラメータが変わるたびに、我々の行動を出すために、反応からUseEffectフックを使用しています.我々のパラメータを変更するたびに、私たちはUseEffectsを呼び出しているので、我々の要求は、ユーザーがより多くのパラメータを入力している場合、我々の要求プロセスをキャンセルする必要があります.Axiosからキャンセルトークンを設定し、それが発生したときにUseEffectを返します.あなたはAxios からキャンセルトークンについての詳細を読むことができます.
データの最初のバッチを取得した後、私たちは1つのより高いページの番号と同じパラメータを使用して別の要求をする必要があります.次のページに返されたデータがあるかどうかは、ここでは我々のNextRageページのアクションをtrueまたはfalseに送信されます.ここでフックがどのように見えますか
// useFetchHooks.js

...

export default function useFetchGames(params, page) {
  const [state, dispatch] = useReducer(reducer, { games: [], loading: true });

  useEffect(() => {
    const cancelToken1 = axios.CancelToken.source();
    dispatch({ type: ACTIONS.MAKE_REQUEST });
    axios
      .get(BASE_URL, {
        cancelToken: cancelToken1.token,
        params: { pageNumber: page, ...params },
      })
      .then((res) => {
        dispatch({ type: ACTIONS.GET_DATA, payload: { games: res.data } });
      })
      .catch((e) => {
        if (axios.isCancel(e)) return;
        dispatch({ type: ACTIONS.ERROR, payload: { error: e } });
      });

    const cancelToken2 = axios.CancelToken.source();
    axios
      .get(BASE_URL, {
        cancelToken: cancelToken2.token,
        params: { pageNumber: page + 1, ...params },
      })
      .then((res) => {
        dispatch({
          type: ACTIONS.NEXT_PAGE,
          payload: { hasNextPage: res.data.length !== 0 },
        });
      })
      .catch((e) => {
        if (axios.isCancel(e)) return;
        dispatch({ type: ACTIONS.ERROR, payload: { error: e } });
      });

    return () => {
      cancelToken1.cancel();
      cancelToken2.cancel();
    };
  }, [params, page]);

  return state;
}

...

here
フェッチフックのテスト
私たちの主なアプリに戻りましょう.JSインポートusefetchgame.我々は、USENTフックをインポートする必要があります.空のオブジェクトとデフォルトのページに0のパラメータを設定します.
その後、私たちのパラメータとページ番号を我々のusefetchgamesフックに渡すことができます.UseFetchGamesは、ゲームの配列、読み込み状態、可能性のあるエラーメッセージ、および別のデータページがAPIから取得できるかどうかを返します.テストとして結果を記録することができます.今すぐアプリを実行する場合は、デフォルトの結果は、私たちのコンソールに人口を見ることができます.
// App.js

...

import { useState } from "react";
import useFetchGames from "./helpers/useFetchGames";

function App() {
  const [params, setParams] = useState({});
  const [page, setPage] = useState(0);
  const { games, loading, error, hasNextPage } = useFetchGames(params, page);

  console.log(games, loading, error, hasNextPage);

  return (
    <div>
      <h1>Seach Steam Sales</h1>
      <h5>
        Powered by <a href="https://apidocs.cheapshark.com/">CheapShark API</a>
      </h5>
    </div>
  );
}

export default App;

...


検索フォームの設定
今、私たちもユーザーは、特定のデータを取得するパラメータを変更することはできませんので、私たちのUIを構築しましょう.まず、いくつかのユーザーコンポーネントを簡単にテンプレート化できるように、反応ブートストラップパッケージをインストールします.

npm i react-bootstrap

次に、SearchFormという新しい機能コンポーネントを作成します.我々のプロジェクトの新しいコンポーネントディレクトリの下のJS.ここではいくつかのパラメータ検索の例を示します.
で見つかったパラメータ名にマッチする各検索コンポーネントの名前要素を指定してください.私は、例としてタイトル、upperpriceとlowerpriceを使用しました.
// SearchForm.js

...

import React from "react";
import { Form, Col } from "react-bootstrap";

export default function SearchForm({ params, onParamChange }) {
  return (
    <Form className="mb-4">
      <Form.Row className="align-items-end">
        <Form.Group as={Col}>
          <Form.Label>Title</Form.Label>
          <Form.Control
            placeholder="eg. Bioshock"
            onChange={onParamChange}
            value={params.title}
            name="title"
            type="text"
          />
        </Form.Group>
        <Form.Group as={Col}>
          <Form.Label>Highest Price</Form.Label>
          <Form.Control
            placeholder="eg. 29.99"
            onChange={onParamChange}
            value={params.upperPrice}
            name="upperPrice"
            type="text"
          />
        </Form.Group>
        <Form.Group as={Col}>
          <Form.Label>Lowest Price</Form.Label>
          <Form.Control
            placeholder="eg. 5.99"
            onChange={onParamChange}
            value={params.lowerPrice}
            name="lowerPrice"
            type="text"
          />
        </Form.Group>
      </Form.Row>
    </Form>
  );
}


...

私たちのアプリに戻りましょう.JSと我々のパラメータ変更のためのハンドラ関数を作成します.で検索します.jsはパラメータの名前をAPIで見つけたパラメータと一致させるように設定しましたので、パラメータを配列に設定できます.この美しさは、我々はアプリを展開することができますし、モジュラー形式で簡単に検索パラメータを追加します.あなたがする必要があるのは、別の要素をsearchformに追加することです.私たちのAPIに一致する名前パラメータを持つjs.
また、検索結果が変更されたときにページの結果を0に戻すように設定します.次に、パラメーターとハンドラ関数を両方の検索フォームコンポーネントに渡します.今、我々は検索にパラメータを加えて、コンソールで記録されるのを見ることができます.
// App.js

...

  const handleParamChange = (e) => {
    const param = e.target.name;
    const value = e.target.value;
    setPage(0);
    setParams((prevParams) => {
      return { ...prevParams, [param]: value };
    });
  };

...

<SearchForm params={params} onParamChange={handleParamChange} />

...

CheapShark API
結果の表示
検索パラメータを変更し、効果的にAPIからデータを取得できるようになりました.別の機能的なコンポーネントのゲームを作成します.APIからのゲームオブジェクトを小道具として取り込むJS.Cheapshark API をチェックアウトするゲームのメタデータをどのように動作する必要があります.
ここでは、ゲームのタイトル、販売価格、リリース日、およびSteam Storefrontのゲームへのリンクを表示する例です
// Game.js

...

import React, { useState } from "react";
import { Card, Button, Collapse } from "react-bootstrap";

export default function Game({ game }) {
  const [open, setOpen] = useState(false);

  return (
    <Card className="mb-3">
      <Card.Body>
        <div className="d-flex justify-content-between">
          <div>
            <Card.Title>
              {game.title} -{" "}
              <span
                className="text-muted font-weight-light"
                style={{ textDecoration: "line-through" }}
              >
                ${game.normalPrice}
              </span>
              <span>{" - "}</span>
              <span className="font-weight-light">${game.salePrice}</span>
            </Card.Title>
            <Card.Subtitle className="text-muted mb-2">
              Release Date:{" "}
              {new Date(game.releaseDate * 1000).toLocaleDateString()}
            </Card.Subtitle>
          </div>
          <img
            className="d-none d-md-block"
            height="50"
            alt={game.title}
            src={game.thumb}
          />
        </div>
        <Card.Text>
          <Button
            onClick={() => setOpen((prevOpen) => !prevOpen)}
            variant="primary"
          >
            {open ? "Hide Details" : "View Details"}
          </Button>
        </Card.Text>
        <Collapse in={open}>
          <div className="mt-4">
            <div>Metacritic Score: {game.metacriticScore}</div>
            <Button
              variant="success"
              href={`https://store.steampowered.com/app/${game.steamAppID}/`}
            >
              Check it out
            </Button>
          </div>
        </Collapse>
      </Card.Body>
    </Card>
  );
}

...

今我々は我々のアプリに当社のゲームコンポーネントをインポートすることができます.js我々は、我々のアプリのデータを取得している間、我々は我々のゲームのコンポーネントを含めることができますし、スピナーを表示するように反応ブートストラップからコンテナとスピナーコンポーネントをインポートします.また、if文を追加することで、APIが発生した場合にエラーを表示できます.
// App.js
...

import { Container, Spinner } from "react-bootstrap";
import Game from "./components/Game";

...

  {loading && <Spinner animation="border" variant="primary" />}
  {error && <h1>{error.message}</h1>}
  {games.map((game) => {
    return <Game key={game.steamAppID} game={game} />;
  })}

...

docs
ページ
最後に、ページの検索結果の複数のページを閲覧できるようにページ付けを作成します.ページネーションを作成します.コンポーネントディレクトリの下のJSファイル.この機能コンポーネントはページ、setPage、およびHasnextPageを小道具として受け取ります.
ページのプロップに基づいてロジックを作成することができますページプロップに基づいてロジックを作成することができます最初のページを過ぎてナビゲートしている場合は、次のページのボタンを表示します.また、ページプロップに基づいてロジックを使用して、ユーザーが2番目のページを過ぎてナビゲートしている場合のみ、省略符号コンポーネントを表示できます.
ユーザーのOnClick要素を渡して、ページのプロップを上下に調整する機能を必要とします.ユーザーはクリックしたい要素を基にします.この関数はsetpage propを使用するために適切な増分または減算を行います.setPageのpropが呼び出されると、usefetchgameで使用します.JSは次の2つのAPI呼び出しをするために行動を送ります.
// Pagination.js

import React from "react";
import { Pagination } from "react-bootstrap";

export default function GamesPagination({ page, setPage, hasNextPage }) {
  const adjustPage = (amount) => {
    setPage((prevPage) => prevPage + amount);
  };

  return (
    <Pagination>
      {page !== 0 && <Pagination.Prev onClick={() => adjustPage(-1)} />}
      {page !== 0 && (
        <Pagination.Item onClick={() => setPage(0)}>1</Pagination.Item>
      )}
      {page > 1 && <Pagination.Ellipsis />}
      {page > 1 && (
        <Pagination.Item onClick={() => adjustPage(-1)}>{page}</Pagination.Item>
      )}
      <Pagination.Item active>{page + 1}</Pagination.Item>
      {hasNextPage && (
        <Pagination.Item onClick={() => adjustPage(1)}>
          {page + 2}
        </Pagination.Item>
      )}
      {hasNextPage && <Pagination.Next onClick={() => adjustPage(1)} />}
    </Pagination>
  );
}
今、我々はインポートすることができますし、我々のアプリにPaginationコンポーネントを追加します.jsとページ、setpage、およびhasnextpage小道具を渡します.私は、ユーザーが上または下から移動できるように私たちのゲームのコンポーネントの上下に私の場所を置いている.
// App.js

...

import Pagination from "./components/Pagination";

...

  <SearchForm params={params} onParamChange={handleParamChange} />
  <Pagination page={page} setPage={setPage} hasNextPage={hasNextPage} />
  {loading && <Spinner animation="border" variant="primary" />}
  {error && <h1>{handleError(error)}</h1>}
  {games.map((game) => {
    return <Game key={game.steamAppID} game={game} />;
  })}
  <Pagination page={page} setPage={setPage} hasNextPage={hasNextPage} />

...


おめでとう!
今、あなたは蒸気のゲーム販売を閲覧する簡単なアプリがあります.あなたが完全なコードを見たり、あなた自身のフォークを作成したいなら、私のレポ をチェックしてください.