Reactでポケモン図鑑を作成する(Step2:ポケモンの一覧を取得する)

## はじめに
前回記事の続きです。
[Step1:API通信](https://qiita.com/koji0705/items/fc7569af1d81e5607339)
[Step2:ポケモンの一覧を取得する]←いまここ。

## 成果物
![pokemons.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/2f5b8c12-9bf9-dfee-f8a3-9cc15b0f83cf.gif)

https://github.com/kouji0705/react_pokemon/pull/2

## 実装
```tsx:src/components/PokemonComponent.tsx
import React from 'react';
import { usePokemons } from '../api/pokemon/api';
import { isNullish } from '../common/common';

const PokemonComponent: React.FC = () => {
  const { listQuery, detailsQueries } = usePokemons(20, 0);

  if (isNullish(detailsQueries)) return <div>Loading...</div>;
  // 一覧データのローディング中
  if (
    listQuery.isLoading ||
    isNullish(listQuery.data) ||
    isNullish(detailsQueries)
  ) {
    return <div>Loading pokemons...</div>;
  }

  // 一覧データ取得時のエラー
  if (listQuery.isError) {
    return <div>Error: {listQuery.error.message}</div>;
  }

  return (
    <div>
      <h2>Pokemon List</h2>
      <ul>
        {detailsQueries.map((query) => {
          if (query.isLoading || isNullish(query.data)) {
            return <div>Loading...</div>;
          }
          return (
            <li key={query.data?.id}>
              <div>{query.data?.id}</div>
              <div>{query.data?.name}</div>
              <div>
                <img
                  src={query.data.sprites.front_default || ''}
                  alt={query.data.name}
                />
              </div>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

export default PokemonComponent;
```

```tsx:src/api/pokemon/api.ts
import { Pokemon } from '@bgoff1/pokeapi-types';
import { UseQueryResult, useQueries, useQuery } from '@tanstack/react-query';
import axios from 'axios';
import { PokemonListResponse, UsePokemonsResult } from './type';

export const getPokemon = async (name: string): Promise<Pokemon> => {
  const response = await axios.get<Pokemon>(
    `https://pokeapi.co/api/v2/pokemon/${name}`
  );
  return response.data;
};

// ポケモンデータを取得するカスタムフック
export const usePokemon = (name: string): UseQueryResult<Pokemon, Error> => {
  return useQuery<Pokemon, Error>({
    queryKey: ['pokemon', name],
    queryFn: () => getPokemon(name),
  });
};

export const getPokemons = async (
  limit = 20,
  offset = 0
): Promise<PokemonListResponse> => {
  const response = await axios.get<PokemonListResponse>(
    `https://pokeapi.co/api/v2/pokemon/?limit=${limit}&offset=${offset}`
  );
  return response.data;
};

export async function getPokemonDetails(url: string): Promise<Pokemon> {
  const { data } = await axios.get(url);
  return data;
}

export const usePokemons = (
  limit: number,
  offset: number
): UsePokemonsResult => {
  const listQuery = useQuery<PokemonListResponse, Error>({
    queryKey: ['pokemonList', limit, offset],
    queryFn: () => getPokemons(limit, offset),
  });

  // useQueriesは常に呼び出されるが、listQuery.dataが存在する場合のみクエリが有効になる
  const detailsQueries = useQueries({
    queries:
      listQuery.data?.results.map((pokemon) => ({
        queryKey: ['pokemonDetails', pokemon.name],
        queryFn: () => getPokemonDetails(pokemon.url),
        enabled: !!listQuery.data, // listQuery.dataが存在する場合のみクエリを有効化
      })) ?? [],
  });

  return { listQuery, detailsQueries };
};

```
:::note info
一覧取得時はポケモンの詳細がないため、もう一度APIを叩き直す必要があります。
:::

```tsx:src/api/pokemon/type.ts
import { Pokemon } from '@bgoff1/pokeapi-types';
import { UseQueryResult } from '@tanstack/react-query';

export interface PokemonListResponse {
  results: Array<{ name: string; url: string }>;
}

export interface UsePokemonsResult {
  listQuery: UseQueryResult<PokemonListResponse, Error>; // 一覧取得のクエリ結果
  detailsQueries: UseQueryResult<Pokemon, Error>[]; // 各ポケモンの詳細情報取得のクエリ結果の配列
}

```