React（Tailwindcss + TypeScript）の構成でアプリを作成しました【Movie App】

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

```
$ npx create-react-app <プロジェクト名> --template typescript
% cd <プロジェクト名>
% yarn start
```

② 必要な環境を整える。

[ESLintとPrettierとを基本からまとめてみた【React＋TypeScriptのアプリにESLintとPrettierを導入】](https://qiita.com/kanfutrooper/items/afd44e1a339ff6963ebf)

```
$ npm i -D tailwindcss postcss autoprefixer
$ npm i tailwind-scrollbar-hide
$ npx tailwindcss init -p
```
```
$ yarn add axios
$ yarn add react-router-dom
$ yarn add react-paginate
```

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

```
App.test.ts
logo.svg
reportWebVitals.ts
setupTests.ts
```

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

```
  src
   ├─ components
         ├── Movie.tsx
         └── Navbar.tsx
   ├── App.tsx
   ├── index.css
   └── index.tsx
├── .env
├── tailwind.config.ts
```
```src/components/Movie.tsx

import React from 'react';

const IMG_API = 'https://image.tmdb.org/t/p/w1280';

type Props = {
  title: string;
  poster_path: string;
  overview: string;
};

const Movie: React.FC<Props> = ({ title, poster_path, overview }) => {
  return (
    <div className='.flex flex-wrap .m-3 w-80 relative group overflow-hidden'>
      <img
        className='max-w-full pt-10'
        src={IMG_API + poster_path}
        alt={title}
      />
      <div className='movie-info'>
        <h1 className='text-xl font-bold'>『 {title} 』</h1>
      </div>

      <div className='absolute bottom-0 left-0 right-0 p-4 text-white transition ease-in-out delay-300 translate-y-full bg-black group-hover:translate-y-0'>
        <h2 className='text-xl'>Overview</h2>
        <p>{overview}</p>
      </div>
    </div>
  );
};

export default Movie; 
```

```src/components/NabBar.tsx
const NavBar: React.FC = () => {
  return (
    <div className='w-full'>
      <div className='text-white text-2xl font-bold md:text-4xl lg:text-5xl ml-4 pt-4'>
        Front-end Moivies App
      </div>
    </div>
  );
};

export default NavBar;
```
```src/App.tsx
import { useEffect, useState } from 'react';
import Movie from './components/Movie';
import NavBar from './components/NavBar';
import axios from 'axios';
import loading_spinner from './assets/loading_spinner.gif';
import ReactPaginate from 'react-paginate';

async function getMovies(pageNo: number) {
  const res = await axios.get(
    `https://api.themoviedb.org/3/trending/movie/day?api_key=${process.env.REACT_APP_API_KEY}&page=${pageNo}`
  );
  console.log(res.data.results);
  return res.data.results;
}

function App() {
  const [movies, setMovies] = useState('Loading');
  const [pageNo, setPageNo] = useState(1);

  useEffect(() => {
    getMovies(pageNo)
      .then((res) => {
        setMovies(res);
      })
      .catch((err) => {
        alert(err);
      });
  }, [pageNo]);

  if (movies === 'Loading' || !movies || movies.length === 0)
    return (
      <div className='flex items-center justify-center h-screen bg-gray-200'>
        <img src={loading_spinner} alt='loading' height='200px' width='200px' />
      </div>
    );
  else
    return (
      <div className='flex flex-col h-full max-h-screen bg-black'>
        <NavBar />
        <div className='w-[250] mt-5 pb-10 font-bold pt-8'>
          <ReactPaginate
            previousLabel={'Previous'}
            nextLabel={'Next'}
            breakLabel={'...'}
            pageCount={1000}
            marginPagesDisplayed={3}
            pageRangeDisplayed={3}
            onPageChange={(e) => setPageNo(e.selected + 1)}
            containerClassName={
              'text-white justify-center items-center flex gap-x-5 gap-y-1.5'
            }
            pageClassName={
              'inline-flex justify-center items-center h-10 w-10 text-base font-bold bg-white rounded-full hover:border-black hover:font-bold '
            }
            pageLinkClassName={
              'inline-flex justify-center rounded-full align-middle text-black'
            }
            breakClassName={
              'inline-flex justify-center items-center h-10 w-10 text-base font-bold bg-white rounded-full'
            }
            breakLinkClassName={
              'inline-flex justify-center rounded-full align-middle text-black'
            }
            activeClassName={'bg-green-400'}
          />
        </div>
        <div className='box-border bg-black text-white font-sans .m-0 grid grid-cols-5 gap-5 items-center'>
          {movies.length > 0 &&
            Array.isArray(movies) &&
            movies.length > 0 &&
            movies.map((movie: any) => <Movie key={movie.id} {...movie} />)}
        </div>
      </div>
    );
}

export default App;
```
```src/index.css
@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap');
@tailwind base;
@tailwind components;
@tailwind utilities;

html {
  background-color: black;
}
```

```.gitignore
//省略

.env //追加する
```
```.env
REACT_APP_API_KEY= APIKEYを入力;
```
```tailwind.config.js
module.exports = {
  content: ['./src/**/*.{tsx,ts}'],
  theme: {
    extend: {},
  },
  plugins: [require('tailwind-scrollbar-hide')],
};
```

## 参考サイト
[Movies App - React Project](https://www.youtube.com/watch?v=sZ0bZGfg_m4)
[react-paginateを使用してページネーション用コンポーネントを作成する
](https://maasaablog.com/development/react/3075/)
[React js Pagination With API Call Using React-paginate](https://www.youtube.com/watch?v=kMuRr53RjcE)