はじめに
前回記事の続きです。
Step1:API通信
[Step2:ポケモンの一覧を取得する]←いまここ。
成果物
実装
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;
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 };
};
一覧取得時はポケモンの詳細がないため、もう一度APIを叩き直す必要があります。
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>[]; // 各ポケモンの詳細情報取得のクエリ結果の配列
}