22
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

はじめに

今までフロントエンドの技術を中心に勉強しており、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サイト作成を行いました。
トラハックさん、上質な講座をありがとうございます。

使用技術

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

  • 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というライブラリを使用し、複数の画像をスライダーで閲覧できるようになっています。
サイズを選択して、ショッピングカートへの追加やお気に入りへの追加を行うことができます。

[お気に入り機能gif]
favoritea.gif

ショッピングカート/購入機能

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

購入の処理時には、Firestoreのトランザクションを使用しており、処理が失敗するとロールバック(全ての購入処理をリセット)されるようになっています。これにより商品の在庫や購入履歴等でのデータの乖離を阻止し、よきせぬトラブルに備えています。

管理者機能

管理者でログインした時に、商品の登録、編集、削除が行えます。
商品の商品画像追加時には、FirebaseのStorageに画像がアップロードされます。

[管理者の時のみ編集用メニュー表示]
スクリーンショット 2021-12-22 7.24.00.png

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

プロフィールページ

登録されている会員情報が見れるページです。
ここで住所や電話番号、メールアドレスなどを変更することもできます。

レスポンシブ対応

レスポンシブにも対応し、スマホでもデスクトップでも快適に閲覧できるようにしました。
デスクトップ版では左側にメニュー、右側にバナーを配置した3カラム構成に。
スマホ版ではドロワーメニューを実装しました。

[ドロワーメニューgif]
drawer.gif

その他機能

  • 通知バナーでの通知
          

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

  • ログインフィルター

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

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

スクリーンショット 2021-12-22 7.24.00.png

Reduxについて軽くまとめ

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

Reduxとは

Reduxは、Reactが扱うstate(状態)を管理する状態管理ライブラリです。
データフロー設計の一つである、Fluxの思想を適用しています。

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

[Reduxにおけるデータフロー]
スクリーンショット 2021-12-07 17.40.49.png

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

ディレクトリ構成

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

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

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

ファイル
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にデータが渡されます。

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を渡します。

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というライブラリを使用して、非同期処理を制御します。データベースへのアクセス時などです。

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

selectors.ts

ここでは、Storeで管理しているstateを参照する関数を定義します。reselectというnpmモジュールを使って実装します。

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とは、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のバージョンが動画の時期から上がりv9になっており、書き方が大幅に変わっていたため、ドキュメントと睨めっこしながら記述するのが大変でした。

22
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?