【初心者🔰】React(TypeScript)を用いてAPIからデータを取得してみる

## はじめに
本記事では、PokeAPIを利用してポケモンの名前を選択すると対応する画像を表示する簡単なアプリを作成します。これを通じて、APIを使ったデータ取得の経験を積み、Reactを用いたAPI通信の基本操作に慣れることを目指します。

今回はコードの具体的な説明はせず、APIを使うイメージを掴んでもらうことに重点を置いています。途中で何をしているのかわからなくなってしまった場合でも、最終系のコードを載せていますので動作の確認まで実施し、アプリが動く喜びを体感しましょう！

:::note info
筆者はReactを学び始めて1年の初心者です。そのため、この記事はReact初心者向けの内容となっています。一緒に学びながら取り組んでいきましょう！
:::




## 事前知識
本記事ではReact、APIについてある程度の事前知識を必要とします。
APIについては参考記事を載せておきますので、必要に応じて一読ください。

https://qiita.com/rk2530/items/48d6100a36484e3b1ee1

https://pokeapi.co/docs/v2#pokemon



## 開発環境の準備
今回、開発をスムーズに始められるようテンプレート用意しました。

https://github.com/ReactLearningDepot/poke-api-practice

ターミナルより、任意のディレクトリで上記のリポジトリをcloneしてください。
コマンドは以下です。
```
git clone https://github.com/ReactLearningDepot/poke-api-practice.git
```
cloneが完了したら、webディレクトリに移動し、プロジェクトで必要となるパッケージをインストールします。
```
cd poke-api-practice/web
npm install
```
以上で開発準備は完了です。



## PokeAPIを使ったポケモンデータ取得
### オープンソースによる取得
それでは、本題に入ります。
コードを書き始める前に、APIを使ってデータを取得するイメージを掴むため、PokeAPIが提供する無料のオープンソースでポケモンデータを取得してみましょう。
以下のサイトにアクセスしてください。

https://pokeapi.co/

今回は最初にポケモンデータの一覧を取得します。
フォームにpokemonと入力し、submitを押します。

![スクリーンショット 2024-12-07 11.56.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128432/4569e0bf-9534-7f2f-407f-bce888ca203a.png)

画像の通り、レスポンスとして結果が返ってきたと思います。ここでresultsタブの直下にポケモンデータの一覧が格納されていることを覚えておいてください。ポケモンデータには名前とURLが記載されています。

それでは一覧の一個目に表示されているポケモンのURLをもう一度フォームに打ってみましょう。
フォームにはパスのみ入力が求められているため、pokemon/1と入力します。

![スクリーンショット 2024-12-07 12.08.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128432/beb774a0-8afd-2f1a-b183-afdfe192b1e0.png)

すると、先ほどとは異なる結果が返ってきました。
どんな情報が返ってきているかわかりにくいですが、パスに数字を指定したことであるポケモンに関するなんらかの情報が返ってきていることが想像できます。

:::note info
フォームに指定するパス(エンドポイント)とレスポンスの対応については本記事の事前知識に掲載したPokeAPIのドキュメントに記載があります。
:::



## アプリケーションによる取得
ここまででPokeAPIを使ってポケモンデータを取得するイメージがついたと思います。
ここからは同様の操作を実際にアプリケーション作成して行っていきます。

### App.tsxの実装（ポケモンの名前を取得）
App.tsxを開いてください。
はじめに、useStateを記述します。
useStateはポケモンデータの状態を保持し、その変更に応じて画面を更新する仕組みを提供します。ここで、取得したデータの型としてGetPokemonTypeを作成し、useStateには配列で型を指定します。
GetPokemonTypeの中身は、前述のポケモンデータに含まれる名前とURLを定義します。

```typescript
export default function App() {
  const [pokemons, setPokemons] = useState<GetPokemonType[]>([])

  return (
    <>
    </>
  )
}

type GetPokemonType = {
  name: string,
  url: string
}
```

次に、`useEffect`を用いてページがレンダリングした際に実行する処理を記述していきます。
ReactでAPI通信を行う手段の1つとして`fetch`を使った方法がありますので今回は`fetch`を使用します。

以下のコードを記述してください。
`fetch`の引数にはオープンソースで入力したURLを指定します。
その後に続く`then`は非同期処理と呼ばれるものですが、ここでは説明を省略します。もしわからない場合は再度Reactの基礎学習に取り組んでみましょう。
setPokemonsを実行することで取得したポケモンデータを保持します。
ここでセットしている`results`がPokeAPIのオープンソースでも確認したキーです。

```typescript
useEffect(() => {
    fetch('https://pokeapi.co/api/v2/pokemon')
      .then((res) => res.json())
      .then((data) => setPokemons(data.results))
  }, []);
```

次に、取得したデータを表示するviewを実装していきます。
ポケモンデータを保持している`pokemons`は配列のため、高階関数`map`を用いてポケモンの名前をリストで表示させます。
`return`に以下のコードを記述してください。

```typescript
return (
  <>
    <ul>
      { pokemons.map((pokemon) => (
        <li key={pokemon.name}>
          {pokemon.name}
        </li>
      ))}
    </ul>
  </>
)
```

ここまででポケモンの名前をリストで表示させることができているはずです。
下記コマンドでサーバーを起動し、ブラウザで確認してみましょう。
```
npm run dev
```

![スクリーンショット 2024-12-07 8.59.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128432/838b13fc-8334-8a91-da7b-b25fa28bbd42.png)

画像のようにポケモンの名前がリストで表示されていることがわかります。

:::note info
取得されるデータ数はデフォルトで20個です。
データ数を指定して取得する場合は以下のようにクエリパラメータにlimitを指定します
https://pokeapi.co/api/v2/pokemon?limit=100
:::


参考に、ここまでのコードは以下のようになっています。

```typescript
import {useEffect, useState} from "react";
import './App.css'

export default function App() {
  const [pokemons, setPokemons] = useState<GetPokemonType[]>([])

  useEffect(() => {
    fetch('https://pokeapi.co/api/v2/pokemon')
      .then((res) => res.json())
      .then((data) => setPokemons(data.results))
  }, []);

  return (
    <>
      <ul>
        { pokemons.map((pokemon) => (
          <li key={pokemon.name}>
            {pokemon.name}
          </li>
        ))
        }
      </ul>
    </>
  )
}

type GetPokemonType = {
  name: string,
  url: string
}
```

### App.tsxの実装（選択したポケモンの画像を取得）
続いて、ポケモンの名前を選択した際に、対象の画像を取得して表示していきます。
それでは、名前を選択した時に画像を取得する処理を行う関数と、取得した画像情報を管理する仕組みを`useState`で実装しましょう。その際、`useState`に指定する型として`GetPokemonSpritesType`も定義します。
関数`handleSelectPokemon`は、画像取得に必要なパス（エンドポイント）を持つURLを引数として受け取り、このURLを用いて再び`fetch`を行います。

```typescript
const [pokemonSprites, setPokemonSprites] = useState<GetPokemonSpritesType | null>(null)

const handleSelectPokemon = (url: string) => {
  fetch(url)
    .then((res) => res.json())
    .then((data) => setPokemonSprites(data['sprites']['other']['official-artwork']))
}
```

```typescript
type GetPokemonSpritesType = {
  front_default: string,
}
```

画像の取得ができたので画像を表示するviewを実装していきます。
`return`の中で`li`タグをクリックした際に先ほどの関数を呼び出す処理として`onClick`を記述してください。
また、画像を表示する`img`タグも記述します。
ここで、`className`が追加になっていますが、今回は事前に`App.css`で簡単なスタイルを指定しており、classNameを記述するだけでCSSが適用されるようになっています（importが必要です）。

```typescript
return (
　<div className='container'>
    <ul>
      {pokemons.map((pokemon) => (
        <li key={pokemon.name} onClick={() => handleSelectPokemon(pokemon.url)}>
          {pokemon.name}
        </li>
      ))
      }
    </ul>
    <div className='pokemon-image'>
      {pokemonSprites && <img src={pokemonSprites?.front_default} alt="pokemon-image"/>}
    </div>
  </div>
)
```

以上で実装は完了です。
参考に、ここまでのコードは以下のようになっています。

```typescript
import {useEffect, useState} from "react";
import './App.css'

export default function App() {
  const [pokemons, setPokemons] = useState<GetPokemonType[]>([])
  const [pokemonSprites, setPokemonSprites] = useState<GetPokemonSpritesType | null>(null)

  const handleSelectPokemon = (url: string) => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => setPokemonSprites(data['sprites']['other']['official-artwork']))
  }

  useEffect(() => {
    fetch('https://pokeapi.co/api/v2/pokemon?limit=20')
      .then((res) => res.json())
      .then((data) => setPokemons(data.results))
  }, []);

  return (
    <div className='container'>
      <ul>
        {pokemons.map((pokemon) => (
          <li key={pokemon.name} onClick={() => handleSelectPokemon(pokemon.url)}>
            {pokemon.name}
          </li>
        ))
        }
      </ul>
      <div className='pokemon-image'>
        {pokemonSprites && <img src={pokemonSprites?.front_default} alt="pokemon-image"/>}
      </div>
    </div>
  )
}

type GetPokemonType = {
  name: string,
  url: string
}

type GetPokemonSpritesType = {
  front_default: string,
}
```

それでは最後にアプリケーションを触ってみましょう。
下記コマンドでサーバーを起動し、ブラウザで確認してみましょう。
```
npm run dev
```

![スクリーンショット 2024-12-07 10.08.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128432/559d7bd9-e9b8-fb93-668b-12646be58e38.png)

画像のようにポケモンの名前を選択するとポケモンの画像が表示されると思います。
お疲れ様でした！

## まとめ
本記事ではPokeAPIを利用して簡単なアプリを作成することで、APIを使ったデータ取得を実践してみました。
ここまで迷わず進められましたでしょうか。今回はコードの具体的な説明は省略し、APIを使うイメージを掴んでもらうことに重点を置いたため、自分で一から書けるか不安に感じる方もいらっしゃるかと思います。
筆者もはじめコードを見た時は何が起こっているのかさっぱりわからず、不安を感じていましたが、まずは完全に理解しようするのではなく、コードを書いたら動いたという経験やその喜びを増やしていくことが大事だと今は思っています。

また、今回は初めて記事の投稿にチャレンジしてみました。
普段仕事などで学んだ技術を資産として残していきたいと思ってのことです。
今後は業務で使用しているReactやSwiftUI、SpringBoot、AWSなどについて、学んだことをアウトプットしていきたいと思います。不慣れが記事に現れることもあるかと思いますが、あたたかく見守っていただけると幸いです。