React(Next.js + Tailwind CSS )の構成 でアプリを作成しました【Build a Pokedex】

## 環境の準備
①ターミナルで、アプリケーションを作成する。

```
$ npx create-next-app@latest
$ cd <プロジェクト名>
$ yarn dev
```

② 必要なパッケージをインストールする。

公式サイト：[Tailwind CSS](https://tailwindcss.com/)

```
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

③ 不要なファイルを削除する。

```
api
styles/home.module.css
```

## コンポーネント・ファイル構成

```
├── components
|    └── Layout.js
├── pages
| 　 ├── pokemon
|    |     └── [id].js
|    ├── _app.js
|    └── index.js
├── package-lock.json
├── package.json
├── postcss.config.js
└── twilwind.config.js
```
公式サイト：[PokeAPI](https://pokeapi.co/)

```components/Layout.js
import Head from 'next/head';

const Layout = ({ title, children }) => {
  return (
    <div className='bg-gray-100'>
      <Head>
        <title>{title}</title>
        <link rel='icon' href='/favicon.ico' />
      </Head>
      <main className='container mx-auto max-w-3xl pt-8 min-h-screen'>
        {children}
      </main>
    </div>
  );
};

export default Layout;
```

```pages/pokemon/[id].js
import Layout from '../../components/Layout';
import Link from 'next/link';

const Pokemon = ({ pokemon }) => {
  const zeroPadding = (num, len) => {
    // 指定した数値の前に指定した桁数分0を追加したあと、後ろから0桁を返す
    return (Array(len).join('0') + num).slice(-len);
  };
  return (
    <Layout title={pokemon.name}>
      <div className='m-auto w-2/3'>
        <div className='flex flex-col  bg-white rounded-md p-8 aspect-square justify-center shadow-lg'>
          <span className='ml-2 font-light'>
            No.{zeroPadding(pokemon.id + 1, 3)}
          </span>

          <img className='mx-auto' src={pokemon.image} alt={pokemon.name} />

          <h2 className='text-2xl mt-6 mb-2 font-bold'>{pokemon.name}</h2>
          {pokemon.types.map((type, index) => (
            <p key={index}>{type.type.name}</p>
          ))}
        </div>
      </div>

      <p className='mt-10 text-center'>
        <Link href='/'>
          <a>
            <button className='focus:outline-none text-white text-sm py-2.5 px-5 rounded-md bg-blue-500 hover:bg-blue-700 hover:shadow-lg'>
              一覧に戻る
            </button>
          </a>
        </Link>
      </p>
    </Layout>
  );
};

//getServerSidePropsを利用してデータを取得しページに表示させる
export const getServerSideProps = async (context) => {
  const { id } = context.query;
  const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
  const pokemon = await res.json();
  const paddedId = ('00' + id).slice(-3);
  pokemon.image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
  return {
    props: { pokemon },
  };
};

export default Pokemon;
```
```pages/index.js
import Layout from '../components/Layout';
import Link from 'next/link';

export default function Home({ pokemon }) {
  /**
   * 足りない桁数を0埋めする関数
   * @param num: 表示させたい数値
   * @param len: 表示させたい桁数
   * @returns {string}
   */
  const zeroPadding = (num, len) => {
    // 指定した数値の前に指定した桁数分0を追加したあと、後ろから0桁を返す
    return (Array(len).join('0') + num).slice(-len);
  };
  return (
    <Layout title='NextJS PokeDex'>
      {/* 　//grid-cols-{n}ユーティリティを使用して、n個の同じサイズの列を持つグリッドを作成 */}
      <div className={'grid grid-cols-3 gap-3'}>
        {pokemon.map((item, index) => (
          <p key={index}>
            <Link href={`/pokemon/${index + 1}`}>
              <a className='border-b-4 py-4 border-grey my-0 hover:shadow-md capitalize flex items-center text-xl font-bold bg-gray-100 shadow-lg'>
                <span className='mr-2 font-light'>
                  {zeroPadding(index + 1, 3)}
                </span>
                <img
                  src={item.image}
                  alt={item.name}
                  className='w-10 h-10 mr-2 font-bold'
                />
                {item.name}
              </a>
            </Link>
          </p>
        ))}
      </div>
    </Layout>
  );
}
//getServerSidePropsを利用してデータを取得しページに表示させる
export const getStaticProps = async () => {
  const res = await fetch('https://pokeapi.co/api/v2/pokemon?limit=150');
  const { results } = await res.json();
  const pokemon = results.map((pokeman, index) => {
    const paddedId = ('00' + (index + 1)).slice(-3);
    const image = `https://assets.pokemon.com/assets/cms2/img/pokedex/detail/${paddedId}.png`;
    return { ...pokeman, image };
  });
  return {
    props: { pokemon },
  };
};        
```
```styles/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
```tailwind.config.js
module.exports = {
  purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};
```


## 参考サイト
[Build a Pokedex with NextJS and Tailwind CSS](https://www.youtube.com/watch?v=sSiKuj1k7hQ)
[Crea una pokedex con React, NextJS y Apollo GraphQL](https://www.youtube.com/watch?v=nlWukvrrBNE)