React API呼び出し

概要: このチュートリアルでは、Webブラウザが提供する組み込みのFetch APIを使用して、ReactアプリでAPIを呼び出す方法を学習します。

Wikipedia検索Reactアプリの紹介

Wikipediaの記事を検索できる新しいReactアプリを作成します。そのためには、ReactアプリはWikipedia Search APIを呼び出す必要があります。

react api call - wikipedia search app

Reactアプリは次のようになります。

react api call - wikipedia search app prototype

このReactアプリは、次のコンポーネントに分割できます。

react api call - wikipedia search

コンポーネントの階層は次のとおりです。

このコンポーネント階層では、

  • Appコンポーネントは、他のコンポーネントを含む親コンポーネントです。
  • SearchBarコンポーネントでは、検索語を入力してEnterキーを押して送信できます。
  • ArticleListコンポーネントは、記事のリストを含む検索結果のレンダリングを担当します。
  • Articleコンポーネントは、検索結果の各記事をレンダリングします。

PropsとStateのデザイン

アプリを作成する前に、いくつか回答が必要な質問があります。

  • アプリケーションの状態とは何ですか?
  • どのコンポーネントがAPI呼び出しを処理する必要がありますか?
  • propsシステムをどのように設計する必要がありますか?

アプリは記事のリストを表示するため、状態変数として記事の配列を持つ必要があります。

const [articles, setArticles] = useState([]);Code language: JavaScript (javascript)

AppコンポーネントはArticleListコンポーネントを使用して記事リストをレンダリングするため、articles配列をpropとしてArticleListに渡す必要があります。

さらに、ArticleListは各articleをpropとしてArticleコンポーネントに渡し、各記事をレンダリングする必要があります。

SearchBarコンポーネントでWikipedia APIを呼び出すと、記事のリストを取得できます。しかし、どのようにSearchBarコンポーネントからAppコンポーネントに記事リストを渡すのでしょうか?

通常、Reactでは、親コンポーネントから子コンポーネントにpropsを渡すことができますが、子コンポーネントから親コンポーネント、または兄弟コンポーネント間では渡すことができません。

この制限を克服するために、AppコンポーネントからSearchBarコンポーネントにコールバック関数をpropとして渡すことができます。

ユーザーが検索フォームを送信すると、コールバック関数onSearchを呼び出して、Appコンポーネントのarticles状態を更新できます。

react api call - states & props design

新しいReactアプリを作成する

まず、コンピューターでターミナルを開き、create-react-appコマンドを使用して新しいReactアプリを作成します。

npx create-react-app wiki-searchCode language: JavaScript (javascript)

次に、srcディレクトリ内のすべてのファイルを削除します。

次に、srcディレクトリに新しいindex.jsファイルを作成し、次のコードを追加します。

import ReactDOM from 'react-dom/client';
import App from './App.js';

const el = document.querySelector('#root');
const root = ReactDOM.createRoot(el);

root.render(<App />);Code language: JavaScript (javascript)

index.jsファイルは、画面にAppコンポーネントをレンダリングします。

その後、srcディレクトリにApp.jsという名前の新しいファイルを作成し、次のコードを追加します。

const App = () => {
  return <div>Wikipedia Search</div>;
};

export default App;Code language: JavaScript (javascript)

最後に、ターミナルで次のコマンドを実行してReactアプリを実行します。

npm startCode language: JavaScript (javascript)

Webブラウザのhttp://localhost:3000に新しいReactアプリが表示されます。

APIの呼び出し

ReactアプリからAPIを呼び出す方法はいくつかあります。最も簡単な方法は、Webブラウザが提供するネイティブのFetch APIを使用することです。サードパーティのパッケージをインストールする必要がないためです。

まず、APIエンドポイントを渡してfetch()メソッドを呼び出します。

const response = await fetch(url);Code language: JavaScript (javascript)

次に、Responseオブジェクトのjson()メソッドを呼び出して、JSON本文の内容を解析します。

const results = await response.json();Code language: JavaScript (javascript)

3番目に、解析されたJSONデータを返します。

return results;Code language: JavaScript (javascript)

Wikipedia Search APIの呼び出し

まず、srcディレクトリにapi.jsという名前の新しいファイルを作成します。

次に、指定された検索語に対してWikipedia Search APIを呼び出し、記事の配列を返すsearch()関数を定義します。

export const search = async (searchTerm) => {
  const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srsearch=${searchTerm}`;
  const response = await fetch(url);
  const results = await response.json();
  return results.query.search;
};
Code language: JavaScript (javascript)

仕組み

ステップ1. 検索語を受け入れる検索関数を定義します。

export const search = async (searchTerm) => {
   // ...
}Code language: JavaScript (javascript)

ステップ2. API URLと検索語を連結してAPIエンドポイントを構築します。

const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;Code language: JavaScript (javascript)

ステップ3. APIからJSONデータを返します。

const response = await fetch(url);
const results = await response.json();
return results.query.search;Code language: JavaScript (javascript)

検索結果には、pageidtitle、およびsnippetが含まれます。 titlesnippetにはHTMLタグが含まれている場合があります。

HTMLタグを取り除くために、srcディレクトリに新しいファイルutil.jsを作成し、stripHTML()関数を次のように定義できます。

export const stripHtml = (html) => {
  let div = document.createElement('div');
  div.innerHTML = html;
  return div.textContent;
};Code language: JavaScript (javascript)

ArticleコンポーネントでstripHTMLを使用して、titlesnippetからHTMLを取り除きます。

Reactコンポーネントの作成

Reactアプリのコンポーネントを作成します。

Appコンポーネント

次のAppコンポーネントには、SearchBarArticleListコンポーネントが含まれています。

import { useState } from 'react';
import { search } from './api';
import SearchBar from './components/SearchBar';
import ArticleList from './components/ArticleList';
import './App.css';
import logo from './wikipedia-logo.png';

const App = () => {
  const [articles, setArticles] = useState([]);

  const handleSearch = async (searchTerm) => {
    const results = await search(searchTerm);
    setArticles(results);
  };

  return (
    <>
      <header>
        <img src={logo} alt="wikipedia" />
        <h1>Wikipedia Search</h1>
        <SearchBar onSearch={handleSearch} />
      </header>
      <main id="searchResult">
        <ArticleList articles={articles} />
      </main>
    </>
  );
};

export default App;Code language: JavaScript (javascript)

仕組み

ステップ1. Appはある程度の状態を保持するため、reactライブラリからuseState関数をインポートします。

import { useState } from 'react';Code language: JavaScript (javascript)

ステップ2. api.jsモジュールからsearch関数をインポートします。

import { search } from './api';Code language: JavaScript (javascript)

ステップ3. SearchBarArticleListコンポーネントをインポートします。

import SearchBar from './components/SearchBar';
import ArticleList from './components/ArticleList';Code language: JavaScript (javascript)

ステップ4. App.cssとwikipedia-logo.pngファイルをインポートします。

import './App.css';
import logo from './wikipedia-logo.png';Code language: JavaScript (javascript)

ステップ5. Appコンポーネントを定義します。

const App = () => {
   // ...
};Code language: JavaScript (javascript)

ステップ5. 記事の配列である状態(articles)を定義し、そのデフォルト値を空の配列に初期化します。

const [articles, setArticles] = useState([]);Code language: JavaScript (javascript)

ステップ6. search関数を呼び出して検索結果を取得し、これらの結果でarticles状態を更新するhandleSearch()関数を定義します。

const handleSearch = async (searchTerm) => {
    const results = await search(searchTerm);
    setArticles(results);
};Code language: JavaScript (javascript)

ステップ7. ロゴ、見出し、SearchBarコンポーネント、およびArticleListコンポーネントを含むJSXを返します。

return (
  <>
    <header>
      <img src={logo} alt="wikipedia" />
      <h1>Wikipedia Search</h1>
      <SearchBar onSearch={handleSearch} />
    </header>
    <main id="searchResult">
      <ArticleList articles={articles} />
    </main>
  </>
);Code language: JavaScript (javascript)

JSXでは、

  • handleSearch関数をSearchBarコンポーネントのonSearch propに渡します。
  • articles状態をpropとしてArticleListコンポーネントに渡します。

読み込み状態の表示

APIを呼び出すと、結果が返されるまでに時間がかかるため、読み込みインディケーターを表示することは、ユーザーエクスペリエンスの向上に重要です。

そのため、Appコンポーネントに新しい状態変数isLoadingを追加します。

const [isLoading, setIsLoading] = useState(false);Code language: JavaScript (javascript)

search関数を呼び出す前に、isLoadingの値をtrueに設定し、結果を取得した後にその値をfalseに設定します。

const handleSearch = async (searchTerm) => {
    setIsLoading(true);
    const results = await search(searchTerm);
    setArticles(results);
    setIsLoading(false);
};Code language: JavaScript (javascript)

読み込みインディケーターを表示するには、次のように条件付きレンダリングを使用します。

{isLoading && <p>Loading...</p>}Code language: HTML, XML (xml)

エラー処理

APIを呼び出すと、ネットワークの切断やサーバーの停止など、さまざまなエラーが発生する可能性があります。そのため、問題が発生した場合にユーザーにエラーメッセージを表示することが重要です。

これを処理するために、Appコンポーネントに新しい状態変数を追加し、エラーが発生するたびに更新できます。

const [error, setError] = useState(null);Code language: JavaScript (javascript)

エラーを処理するために、try…catchステートメントを使用できます。エラーが発生した場合、エラー状態変数を更新できます。

const handleSearch = async (searchTerm) => {
  setIsLoading(true);
  try {
    const results = await search(searchTerm);
    setArticles(results);
  } catch (err) {
    setError('Something went wrong. Please try again.');
  } finally {
    setIsLoading(false);
  }
};Code language: JavaScript (javascript)

このコードでは、catchブロックでエラー状態変数をエラーメッセージに設定します。エラーメッセージをレンダリングするには、条件付きレンダリングを使用できます。

{error && <p className="error">{error}</p>}Code language: HTML, XML (xml)

App.jsコンポーネントの完全なコードは次のとおりです。

import { useState } from 'react';
import SearchBar from './components/SearchBar';
import ArticleList from './components/ArticleList';
import { search } from './api';
import './App.css';
import logo from './wikipedia-logo.png';

const App = () => {
  const [articles, setArticles] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleSearch = async (searchTerm) => {
    setIsLoading(true);
    try {
      const results = await search(searchTerm);
      setArticles(results);
    } catch (err) {
      setError('Something went wrong. Please try again.');
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <>
      <header>
        <img src={logo} alt="wikipedia" />
        <h1>Wikipedia Search</h1>
        <SearchBar onSearch={handleSearch} />
      </header>

      <main id="searchResult">
        {isLoading && <p>Loading...</p>}
        {error && <p className="error">{error}</p>}
        <ArticleList articles={articles} />
      </main>
    </>
  );
};

export default App;Code language: JavaScript (javascript)

ユーザーが新しい検索リクエストを開始した場合に以前の検索リクエストを中止するために、AbortControllerの組み込みを検討することをお勧めします。

SearchBarコンポーネント

SearchBarコンポーネントでは、ユーザーが検索語を入力し、検索のためにAPIを呼び出す関数を実行できます。

import { useState } from 'react';

const SearchBar = () => {
  const [searchTerm, setSearchTerm] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();
    onSearch(searchTerm);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="search"
        name="searchTerm"
        id="searchTerm"
        placeholder="Enter a search term..."
        value={searchTerm}
        onChange={(event) => {
          setSearchTerm(event.target.value);
        }}
      />
    </form>
  );
};

export default SearchBar;Code language: JavaScript (javascript)

仕組み

ステップ1. reactライブラリからuseState関数をインポートします。

import { useState } from 'react';Code language: JavaScript (javascript)

ステップ2. 関数onSearchをpropとして受け入れるSearchBarコンポーネントを定義します。

const SearchBar = ({ onSearch }) => {
   // ..
}Code language: JavaScript (javascript)

ステップ3. SearchBarコンポーネントのsearchTerm状態を定義し、そのデフォルト値を空の文字列に初期化します。

 const [searchTerm, setSearchTerm] = useState('');Code language: JavaScript (javascript)

ステップ4. 送信イベントを処理するイベントハンドラーを作成します。

 const handleSubmit = (e) => {
    e.preventDefault();
    onSearch(searchTerm);
  };Code language: JavaScript (javascript)

送信イベントでは、e.preventDefault()を呼び出して、ユーザーがフォームを送信したときにページ全体がリロードされないようにし、引数としてsearchTerm状態を使用してonSearch関数を呼び出します。

ステップ5. フォームと入力要素を含むJSXを返します。

return (
    <form onSubmit={handleSubmit}>
      <input
        type="search"
        name="searchTerm"
        id="searchTerm"
        placeholder="Enter a search term..."
        value={searchTerm}
        onChange={(e) => {
          setSearchTerm(e.target.value);
        }}
      />
    </form>
  );Code language: JavaScript (javascript)

JSXでは、

  • handleSubmitイベントハンドラーをフォームのonSubmit propに接続します。
  • 入力要素の変更イベントハンドラーでsetSearchTermを呼び出して状態を更新します。 e.target.valueは、入力要素の現在の値を返します。 setSearchTerm関数は、新しい入力値をコンポーネントのsearchTerm状態に代入します。

ステップ6. デフォルトエクスポートを使用してSearchBarコンポーネントをエクスポートします。

export default SearchBar;Code language: JavaScript (javascript)

ArticleListコンポーネント

ArticleListコンポーネントは、Articleコンポーネントのリストを表示します。

import Article from './Article';

const ArticleList = ({ articles }) => {
  const renderedArticles = articles.map((article) => {
    return <Article key={article.pageid} article={article} />;
  });

  return <div>{renderedArticles}</div>;
};

export default ArticleList;Code language: JavaScript (javascript)

仕組み

ステップ1. Articleコンポーネントをインポートします。

import Article from './Article';Code language: JavaScript (javascript)

ステップ2. 記事の配列をpropとして受け取り、Articleコンポーネントを使用して各記事をレンダリングするArticleListコンポーネントを定義します。

const ArticleList = ({ articles }) => {
  const renderedArticles = articles.map((article) => {
    return <Article key={article.pageid} article={article} />;
  });

  return <div>{renderedArticles}</div>;
};Code language: JavaScript (javascript)

ステップ3. ArticleListコンポーネントをデフォルトコンポーネントとしてエクスポートします。

export default ArticleList;Code language: JavaScript (javascript)

Articleコンポーネント

Articleコンポーネントは記事をレンダリングします。

import { stripHtml } from '../util';

const Article = ({ article }) => {
  const url = `https://en.wikipedia.org/?curid=${article.pageid}`;
  const title = stripHtml(article.title);
  const snippet = stripHtml(article.snippet);

  return (
    <article>
      <a href={url} title={title}>
        <h2>{title}</h2>
      </a>
      <div className="summary">{snippet}...</div>
    </article>
  );
};

export default Article;Code language: JavaScript (javascript)

仕組み

ステップ1. utilライブラリからstripHTML関数をインポートします。

import { stripHtml } from '../util';Code language: JavaScript (javascript)

ステップ2. article propをレンダリングするArticleコンポーネントを作成します。

const Article = ({ article }) => {
  const url = `https://en.wikipedia.org/?curid=${article.pageid}`;
  const title = stripHtml(article.title);
  const snippet = stripHtml(article.snippet);

  return (
    <article>
      <a href={url} title={title}>
        <h2>{title}</h2>
      </a>
      <div className="summary">{snippet}...</div>
    </article>
  );
};Code language: JavaScript (javascript)

Articleコンポーネントでは、

  • Wikipediaの記事へのURLを構築します。
  • articleオブジェクトのタイトルとスニペットからHTMLタグを取り除きます。
  • <article> JSX要素を返します。

ステップ3. Articleコンポーネントをデフォルトエクスポートとしてエクスポートします。

export default Article;Code language: JavaScript (javascript)

Wiki Searchソースコードをダウンロードします。

まとめ

  • ネイティブブラウザFetch APIを使用して外部APIを呼び出します。
このチュートリアルは役に立ちましたか?