Reactでポケモン図鑑を作成する(Step4:SearchBarを作成する)

## はじめに
前回記事の続きです。
[Step1:API通信](https://qiita.com/koji0705/items/fc7569af1d81e5607339)
[Step2:ポケモンの一覧を取得する](https://qiita.com/koji0705/items/5a8e85af4167bea0f8d4)
[Step3:CardListを作成する](https://qiita.com/koji0705/items/7a2363ab46f46f3cc4b5)
Step4:SearchBarを作成する←今ここ

## 成果物
![search_bar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/9c118bf6-9697-45a5-78f1-4dbd415068c2.gif)

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

:::note info
今回のポイントは検索バーの責務はクエリパラメータを変更する事のみ。
再レンダリングの制御は、クエリパラメータを監視しているコンポーネント側で制御。
これにより、検索バーとコンポーネントの関係を疎結合？にしている。
:::


## ソースコード
```tsx:src/pages/Home.tsx
import { PokemonComponent } from '../features/components/PokemonComponent';
import { SearchBar } from '../features/components/SearchBar';

export const Home = () => {
  return (
    <div>
      <h1>ポケモン表示</h1>
      <div>
        <SearchBar />
      </div>
      <PokemonComponent />
    </div>
  );
};
```

```tsx:src/features/components/SearchBar.tsx
import React, { useState } from 'react';
import { Button, TextField } from '@mui/material';
import { useNavigate } from 'react-router-dom';

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

  const handleSearchClick = () => {
    navigate(`?search_id=${encodeURIComponent(searchTerm)}`);
  };

  return (
    <div style={{ marginBottom: '20px' }}>
      <TextField
        label="ポケモン検索"
        variant="outlined"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        style={{ marginRight: '10px' }}
      />
      <Button variant="contained" color="primary" onClick={handleSearchClick}>
        検索
      </Button>
    </div>
  );
};
```

```tsx:src/features/components/PokemonComponent.tsx
import React from 'react';
import { usePokemon, usePokemons } from '../../api/pokemon/api';
import { isNullish } from '../../common/common';
import { CardList } from './PokemonCardList';
import { useLocation } from 'react-router-dom';

export const PokemonComponent: React.FC = () => {
  const location = useLocation();
  const queryParams = new URLSearchParams(location.search);
  const searchId = queryParams.get('search_id');
  const searchIdNumber = searchId ? parseInt(searchId) : undefined;
  if (searchIdNumber) {
    return <PokemonOne id={searchIdNumber} />;
  }
  return <PokemonList />;
};

export const PokemonList = () => {
  const { listQuery, detailsQueries } = usePokemons(20, 0);

  if (isNullish(detailsQueries)) return <div>Loading...</div>;
  console.log('listQuery', listQuery);
  console.log('HIT8 detailsQueries', detailsQueries);
  // 一覧データのローディング中
  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>
      <CardList detailsQueries={detailsQueries} />
    </div>
  );
};

type PokemonOneProps = {
  id: number;
};

export const PokemonOne = ({ id }: PokemonOneProps) => {
  const result = usePokemon(id);
  if (result.isLoading || isNullish(result)) {
    return <div>Loading...</div>;
  }
  if (result.isError) {
    return <div>Error: {result.error.message}</div>;
  }
  return (
    <div>
      <h2>Pokemon One</h2>
      <div>{result.data?.name}</div>
      <div>
        {result.data?.sprites.front_default && (
          <img src={result.data?.sprites.front_default} alt="pokemon" />
        )}
      </div>
    </div>
  );
};
```

:::note info
今回のポイントは下記。
SearchBarの変更をuseLocationを利用する事で検知している。
useLocationを利用することで、これまでReactHooks等で渡していたset関数等を挟まずにサイレンダリングを実現している。
:::

## 最後に
ちょっと今回は作ることを優先してコードが雑になってしまいました。
次回以降でリファクタリングを実施していこうと思います！！！