Reduxの勉強用にReactでECサイトを作成しました。

## はじめに

今までフロントエンドの技術を中心に勉強しており、ReactやNext.jsの学習を進めてきました。
その中で、状態管理はContext + Hooksを用いていましたが、この度、「Reduxも学習しよう」と思い、ECサイト作成を通して学習した記録です。

## 作成したもの

React × Redux × Firebaseで、ECサイトを作成しました。
一店舗対お客様の、洋服のオンラインショップです。

ロゴは、某衣料品ブランドのロゴを模して作成しました。

[URL]
https://react-redux-ec.vercel.app/

（ゲストログイン用アカウント）
メールアドレス：guest@example.com
パスワード：guestguest

[github]
https://github.com/suzu1997/react-redux-ec


トラハックさんの以下の動画シリーズを基に、学習・ECサイト作成を行いました。
トラハックさん、上質な講座をありがとうございます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/videoseries?list=PLX8Rsrpnn3IWavNOj3n4Vypzwb3q1RXhr" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## 使用技術
主な使用技術は以下の通りです。

- TypeScript 4.1.2
- React 17.0.2
- react-router 5
- connected-react-router 6.9.2
- Redux 4.1.2
- Firebase 9.6.0
    - Cloud Firestore
    - Firebase Authentication
    - Storage
- Tailwind CSS 
- Material UI(MUI)

## 機能

### サインイン/サインアップ
Firebase Authenticationによってメール/パスワード認証を行なっています。
sendPasswordResetEmailメソッドを用いて、パスワードを忘れた時でも新しいパスワードにすぐ変更できるようにしています。
### 商品一覧ページ
販売中の商品が並んだページ。
メンズ/レディース、トップス・アウターなどのカテゴリごとの絞り込み機能、並び替え機能(更新順、価格の高い順、低い順)、キーワード検索機能をつけました。
### 商品詳細ページ
商品の詳細を載せたページ。
商品画像の表示には[react-id-swiper](https://github.com/kidjp85/react-id-swiper)というライブラリを使用し、複数の画像をスライダーで閲覧できるようになっています。
サイズを選択して、ショッピングカートへの追加やお気に入りへの追加を行うことができます。

[お気に入り機能gif]
<img width=400" alt="favoritea.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007943/4d4d3838-da97-3ecc-32ac-6852daff1043.gif">

### ショッピングカート/購入機能
商品詳細でショッピングカートへ追加した商品をリストで確認、リストからの削除、購入の機能があります。

購入の処理時には、Firestoreのトランザクションを使用しており、処理が失敗するとロールバック(全ての購入処理をリセット)されるようになっています。これにより商品の在庫や購入履歴等でのデータの乖離を阻止し、よきせぬトラブルに備えています。
### 管理者機能
管理者でログインした時に、商品の登録、編集、削除が行えます。
商品の商品画像追加時には、FirebaseのStorageに画像がアップロードされます。

[管理者の時のみ編集用メニュー表示]
<img width=500" alt="スクリーンショット 2021-12-22 7.24.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007943/0a78ff0a-f937-dfad-e25c-6c47ac152b80.png">

(管理者用アカウント)
メールアドレス：admin@gmail.com
パスワード:aaaaaaaa

### プロフィールページ
登録されている会員情報が見れるページです。
ここで住所や電話番号、メールアドレスなどを変更することもできます。
### レスポンシブ対応
レスポンシブにも対応し、スマホでもデスクトップでも快適に閲覧できるようにしました。
デスクトップ版では左側にメニュー、右側にバナーを配置した3カラム構成に。
スマホ版ではドロワーメニューを実装しました。

[ドロワーメニューgif]
![drawer.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007943/e7455526-360b-ba34-8c1d-e4aad5cbf4f5.gif)



### その他機能
- 通知バナーでの通知
　　　　　　

Reactのライブラリである[react-hot-toast](https://github.com/timolins/react-hot-toast)を使用して、ログイン/ログアウト時、カートやお気に入り追加時等に、通知バナーを表示するようにしました。
画面の端から現れて、数秒すると消える通知です。
これにより、ユーザーの操作を邪魔することなく、通知を行うことができます。

- ログインフィルター

ログインしなくても閲覧できるページ、ログイン後しか閲覧できないページを作成しました。
商品一覧や詳細はログインしなくても見ることができます。

ログインしていない状態でお気に入りやカート追加をしようとすると、モーダルで
ログインを促すような仕様にしました。

<img width=500" alt="スクリーンショット 2021-12-22 7.24.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007943/f41f8212-bb01-f841-5b94-0824345012e6.gif">

## Reduxについて軽くまとめ

今回のECサイト作成はReduxを学習することが目的だったので、Reduxについてメモがてら軽くまとめようと思います。

### Reduxとは
[Redux](https://redux.js.org/)は、Reactが扱うstate(状態)を管理する状態管理ライブラリです。
データフロー設計の一つである、Fluxの思想を適用しています。

Fluxとはデータの流れを一方向に限定した考え方で、それによりデータの流れがわかりやすくなり、管理がしやすくなります。それにより、アプリ全体の見通しが良くなるというメリットがあります。

[Reduxにおけるデータフロー]
<img width=500" alt="スクリーンショット 2021-12-07 17.40.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1007943/ab5aa16b-14ed-076f-b797-1d6d71757844.png">

Reduxにおいてもデータは一方向のみに流れ、変更や取得が行われます。


### ディレクトリ構成

Reduxのディレクトリ構成は、大きく3パターンあるようです。

- redux-way : reduxによって導入される概念ごとに分ける
- ducks : redux-wayのAction関連を１つにまとめる stateカテゴリーごとに一つのファイル
- re-ducks : stateのカテゴリーごとにディレクトリを作成し、その中にAction関連ファイルを分割

私はトラハックさんの動画でも紹介されていた、re-ducksパターンで作成しました。
redux-way, ducksの後で、改善されて出てきたパターンがre-ducksパターンであり、中規模・大規模開発に向いているのもre-ducksパターンみたいです。

```typescript:ファイル
src/
├ components
|   ├ ....
|   └ ....
├ containers
|   ├ ....
|   └ ....
└ reducks
    ├ store
    |   ├ initialState.ts
    |   └ store.ts
    |   
    ├ products
    |   ├ actions.ts
    |   ├ operations.ts
    |   ├ reducers.ts
    |   ├ selectors.ts
    |   └ types.ts
    └ users
        ├ actions.ts
        ├ operations.ts
        ├ reducers.ts
        ├ selectors.ts
        └ types.ts
```

### 各ファイル/機能について

#### actions.ts
Actionsとは、アプリ側からの要求を受けて、Storeへ状態の変更の依頼をする役割を担う機能です。
コンポーネントでイベントが発生するタイミングで、action.tsに定義したActions関数が呼ばれ、次のReducersにデータが渡されます。

```typescript:src/reducks/users/actions.ts
export const FETCH_PRODUCTS_IN_CART = 'FETCH_PRODUCTS_IN_CART';

export const fetchProductsInCartAction = (products: Array<ProductInCart>) => {

  // プレーンなオブジェクトを返す
  return {
    // typeとpayloadを記述する
    type: 'FETCH_PRODUCTS_IN_CART',
    payload: products
  }
};
```
どんな種類の変更なのかを表すtypeと、storeの変更に必要なデータであるpayloadをオブジェクト形式で返します。

#### reducers.ts
Reducersとは、Actionsからデータを受け取り、Storeのstateをどう変更するのか決める役割を担う機能です。

実行されたActionを受け取り、Switch構文内でaction.typeに応じてStoreにstateを渡します。

```typescript:src/reducks/users/reducers.ts
import * as Actions from './actions';
import initialState from '../store/initialState';
import { Action } from './types';

// 第一引数にState(現在のstateの状態)。初期状態をデフォルトとして設定
// 第二引数にactionがreturnした値(typeとpayload)
export const UsersReducer = (state = initialState.users, action: Action) => {
  // Actionsのtypeに応じてstateをどう変更するのか決める
  switch (action.type) {
    case Actions.DELETE_FAVORITE_PRODUCTS:
      return {
        ...state,
        favorite: [...action.payload],
      }
    case Actions.FETCH_FAVORITE_PRODUCTS:
      return {
        ...state,
        favorite: [...action.payload],
      }
    case Actions.FETCH_ORDERS_HISTORY:
       ・
　　　　　　　　　　　　　　・
　　　　　　　　　　　　　　・
```

#### operations.ts

ここでは、Storeのデータを変更するための複雑な処理を記述します。
[redux-thunk](https://github.com/reduxjs/redux-thunk)というライブラリを使用して、非同期処理を制御します。データベースへのアクセス時などです。

コンポーネントでイベントが発生するとまず、operationsファイルのメソッドが呼ばれ、その中でActions関数がよばれることになります。

#### selectors.ts
ここでは、Storeで管理しているstateを参照する関数を定義します。[reselect](https://github.com/reduxjs/reselect)というnpmモジュールを使って実装します。

```typescript:src/reducks/users/selectors.ts
import { createSelector } from 'reselect';

const usersSelector = (state) => state.users;

// userIdを参照する関数
export const getUserId = createSelector(
     [ usersSelector ],
     state => state.uid   // state: usersSelectorが返す値(state.users)
);
```
selectorで取得される値は、store内のstateが変更される度にコンポーネント側でも再取得されるため、常に最新のstateがコンポーネント側で取得されます。

#### types.ts
TypeScriptで記述する場合に使うファイルで、型定義を記述します。
action, operation, reducerなどで使う型を定義しておきます。

#### storeディレクトリ
storeディレクトリのinitialState.tsでは、storeのデータの初期値を定義しておきます。
また、store.tsでは、ReduxのstoreとReducersを関連づける処理を行います。

### 使用したライブラリ
#### connected-react-router
[connected-react-router](https://github.com/supasate/connected-react-router)とは、Reduxのstoreでrouterを管理するためのライブラリです。
SPAでURLルーティングを行うためのReactのライブラリ、react-routerをベースにルーティング状態を管理します。

connected-react-routerを使用することで、pushやreplaceといったメソッドを使って画面遷移を行なったり、現在のpath情報を簡単に取得できたりします。

#### redux-thunk
上述の通り、Reduxで非同期処理を行うことのできるミドルウェアです。


### 所感
Reduxを使用すると、たくさんのメソッドを介して状態が変更・管理されることになり、ファイル数が非常に多くなるなーという印象でした。
アプリが大規模になってデータ数やそれを用いるところが多くなると、有用な手段なのかなと思いました。

ただ、個人レベルで何か作成するとなると、多くの記事で記載されているように、Context + hooksで実装するのが簡単でいいかなと感じます。

今後はuseSWRやreact queryでの状態管理も学習していきたい所存です。

## 大変だったこと・苦労したこと

- TypeScriptでの型定義 : 
　Reduxを用いていると、特殊な型定義が必要な場面が多々出てきました。
　間違った型を指定しまうとエラーが出るため、適切な型を調べて付与するのが大変でした。

- Firebaseのバージョンが動画の時期から上がりv９になっており、書き方が大幅に変わっていたため、ドキュメントと睨めっこしながら記述するのが大変でした。