日本一わかりやすいReact-Redux講座 実践編 #1~2 学習備忘録

## はじめに
この記事は、Youtubeチャンネル『トラハックのエンジニア学習ゼミ【とらゼミ】』の『[日本一わかりやすいReact-Redux講座 実践編](https://www.youtube.com/watch?v=8fNgXnVTULU)』の学習備忘録です。

ここから、いよいよ本格的にアプリを開発していきます。

前回記事は[こちら](https://qiita.com/ddpmntcpbr/items/3e4d2bf793c2e0c414c1)。

## 要約
1. ``店舗:ユーザー ＝ 1:n`` のECアプリを開発する
2. ``Firebase Auth``による新規登録、サインイン認証を実装する
3. ``reducksパターン``に沿った関数の実装手順を理解する

## #1 ECアプリの機能とデータ設計

### 実装するアプリ概要
- ファッション系のECアプリ。服や靴などが出品される。
- **店舗:ユーザー = 1:n**
  - 店舗は商品の出品、在庫管理、売り上げ管理ができる
  - ユーザーは商品閲覧、カートへ追加、購入、注文履歴の確認ができる

ユニクロや無印のECアプリのように、商品の出品者が固定の設計（メルカリやヤフオクのように、ユーザー自身が出品者になれるものが、n:n のECアプリ）

### 実装する機能の整理

現時点では完璧には中身を理解できていませんが、後で振り返られるよう、動画内容に沿ってまとめておきます。

#### 認証機能
ユーザーの新規作成、ログイン、ログアウト機能を実装する。``Firebase Auth``を利用します。

#### 認証のリッスン
認証情報をブラウザに残すことで、再ログインの手間を省く。アプリ全体を``Auth``コンポーネントでラッピングして実装します。

#### 商品情報のCRUD
- Create: 商品情報の追加
- Read: 商品情報の読み込み
- Update: 商品情報の更新
- Delete: 商品情報の削除

Firebase の ``Cloud Function`` を DB として利用する。商品情報の操作は、管理者ユーザーしか行えないようにします。

#### react-swiper による画像スライダー
一つの商品情報に対して複数の画像を対応させたい（例えば、同じ型のTシャツの色違い、など）ので、動的な画像スライダーを作ります。

#### Drawer メニュー
ヘッダーコンポーネントとして、表示・非表示を動的に制御できるメニューを作る。``Material-UI`` を活用します。

#### カートへの商品追加
商品をカートに追加することで、カートアイコン上に表示される"アイテム数"を変更させる。``Firestore`` でデータをリッスンします。

#### 商品の注文
トランザクションを実装します（ ``Firestore`` を利用)。加えて、注文履歴の閲覧機能も作ります。

#### タグ検索機能
「メンズ」「レディース」「トップス」「ボトムス」のようなタグを用いた検索機能を作ります。``Firestore indexes`` を活用する。

### データ設計

```:データ設計
1. categoriesコレクション
2. productsコレクション
3. usersコレクション
  ├── 3-1. cartサブコレクション
  └── 3-2. ordersサブコレクション
```

#### categoriesコレクション
商品情報をグループ分けするためのタグの情報を保存する。

#### productsコレクション
商品情報を保存する。

#### usersコレクション
ユーザー情報の保存する。

#### cartsサブコレクション
対応するユーザーのカートも中身の情報を保存する。

#### orders サブコレクション
対応するユーザーの注文履歴の情報を保存する。

## #2 Firebase Authで認証機能を作ろう

### Firebase Authとは
Firebaseの中に含まれている、認証機能を簡単に実装するための機能。非常に少ないコード量で処理が複雑な認証機能を実装できます。

パスワード認証、電話番号認証、各種OAuth認証(gmail, twitter, facebookなど)などの複数の認証方法に対応しています。

今回は最も基本的な``メール／パスワード認証``を実装します。

### ConsoleからAuthcenticationを設定する
まず最初に、Firebase側でFirebase Authの機能を有効します。

Firebaseコンソールから、Authenticationに移動。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/481fd870-ea7d-6e6a-fece-b077bc2cb997.png)

``メール／パスワード``の鉛筆マークから、本機能を有効化します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/4798ff25-bfb8-090f-214f-41e2d86c7013.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/08169d06-1bde-cdf9-040f-a8d9017d71ba.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/481e3d97-e4ae-7358-e835-009e8c78b16e.png)

これで有効化ができました！とっても簡単。

### Firebase用の設定ファイルを作る
ここからは、アプリ側でコードを書いていきます。

まず最初に、有効化したFirebase Auth機能をアプリと連携する設定変数を取得します。

コンソールの歯車マーク->プロジェクトを設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/db0a5fc3-8bf0-be18-86f3-1fc3aa1b4e9f.png)

``Firebase SDK snippet``で``構成``を選択すると、設定変数が取得できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/e640012b-0a08-3a43-38dc-5828be58d57b.png)

アプリ側でfirebase用のファイルを作成します。srcディレクトリ直下に``firebase``ディレクトリを作り、ここに設定用ファイルを定義します。

```
src
 └─ firebase
     ├─ config.js
     └─ index.js  
```

```javascript:src/firebase/config.js
export const firebaseConfig = {
  apiKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  authDomain: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  databaseURL: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  projectId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  storageBucket: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  messagingSenderId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  appId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  measurementId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
};
```

先ほどの設定変数を保存するファイル。文頭で``export``している点は注意。

```javascript:src/firebase/index.js
import firebase from "firebase/app";
import 'firebase/auth';
import 'firebase/firestore';
import 'firebase/storage';
import 'firebase/functions';
import {firebaseConfig} from "./config";

firebase.initializeApp(firebaseConfig);
export const auth = firebase.auth();
export const db = firebase.firestore();
export const storage = firebase.storage();
export const functions = firebase.functions();
export const FirebaseTimestamp = firebase.firestore.Timestamp;
```

- firebaseの各種機能と、先ほど定義した``firebaseConfig``をimportする
- ``firebase.initializeApp(firebaseConfig);``と書くことで、reactアプリとfirebaseインスタンスを接続する（接続先のfirebaseインスタンスを定義する）
- firebaseの各種機能を定数化し、外部ファイルで使用できるようexportする

### ファイルを整理
ここまでの講座で作成したファイルや``create-react-app``で自動作成されたファイルのうち、不要なものを削除しておきます。

```markdown:削除ファイル
## css関連ファイル。cssは別に用意されたものを使うため
src/App.css
src/index.css

## テスト関連ファイル。今回はテストは扱わないため。
src/App.test.js
src/logo.svg
src/setup.js

## 基礎編#10で作成したコンテナーコンポーネント。今後はHooksのみを使用する。
src/containers/index.js
src/containers/Login.js
src/templates/LoginClass.jsx
```

``src/index.js``で、``src/App.css``を読み込む記述が残っているため、削除。

```javascript:src/index.js
import * as History from 'history';
// import './index.css'; // 削除
import App from './App';
import * as serviceWorker from './serviceWorker';
```

本講座のデモアプリが公開されているgithubリポジトリ([https://github.com/deatiger/ec-app-demo](https://github.com/deatiger/ec-app-demo))から、css関連ファイルを含む``src/assets``ディレクトリをダウンロードし、開発アプリの``src``ディレクトリ直下に配置する。

```:assetsディレクトリ
assets
 ├── img
 │   ├── icons
 │   │   └── logo.png
 │   └── src
 │       ├── no-profile.png
 │       └── no_image.png
 ├── reset.css
 ├── style.css
 └── theme.js
```

cssファイルとしては、今後のこの``reset.css``や``style.css``を使用していきます。

これらを読み込む記述を``src/App.jsx``に加えます。

```react:src/App.jsx
import React from 'react'
import Router from './Router'
import "./assets/reset.css"
import "./assets/style.css"
.
.
.
```

### SignUp画面を作る

template として SignUpコンポーネントを作成するにあたり、必要になる UI コンポーネントを先に用意します。

componentsディレクトリの下に``UIkit``ディレクトリを作成し、その中で複数のコンポーネントで使用するための UI コンポーネントをまとめて管理するようにします。

```
components
└── UIkit
    ├── PrimaryButton.jsx // ボタンのUIコンポーネント。SignUp画面では、アカウント登録のボタンとして使う
    ├── TextInput.jsx // テキスト入力フィールド。
    └── index.js // エントリーポイント
```

PrimaryButtonコンポーネント、TextInputコンポーネントは、Material-UIを活用して作成します。

```react:src/components/UIkit/PrimaryButton.jsx
import React from "react";
import Button from "@material-ui/core/Button";
import {makeStyles} from "@material-ui/styles";

const useStyles = makeStyles({
  "button": {
    backgroundColor: "#4dd0e1",
    color: "#000",
    fontSize: 16,
    height: 48,
    marginButton: 16,
    width: 256
  }
})

const PrimaryButton = (props) => {

  const classes = useStyles();

  return(
    <Button className={classes.button} variant="contained" onClick={() => props.onClick()}>
      {props.label}
    </Button>
  )
}

export default PrimaryButton
```

```react:src/components/UIkit/TextField.jsx
import React from 'react';
import TextField from '@material-ui/core/TextField'

const TextInput = (props) => {
  return (
    <TextField
      fullWidth={props.fullWidth}
      label={props.label}
      margin={"dense"}
      multiline={props.multiline}
      required={props.required}
      rows={props.rows}
      value={props.value}
      type={props.type}
      onChange={props.onChange}
    />
  )
}

export default TextInput
```

```react:src/components/UIkit/index.js
export {default as PrimaryButton} from "./PrimaryButton"
export {default as TextInput} from "./TextInput"
```

各UIコンポーネントは、親コンポーネントからもらう props に応じて、諸々のパラメーター(ラベルや行数など)を変えられるようにしてあります。

上記のUIコンポーネントを利用して、SignUpテンプレートを作成します。

```react:src/templates/SignUp.jsx
import React, {useState,useCallback} from "react";
import {PrimaryButton,TextInput} from "../components/UIkit"

const SignUp = () => {

  const [username,setUsername] = useState()
  const [email,setEmail] = useState()
  const [password,setPassword] = useState()
  const [confirmPassword,setConfirmPassword] = useState()

    const inputUsername = useCallback((event) => {
      setUsername(event.target.value)
    },[setUsername])

    const inputEmail = useCallback((event) => {
      setEmail(event.target.value)
    },[setEmail])

    const inputPassword = useCallback((event) => {
      setPassword(event.target.value)
    },[setPassword])

    const inputConfirmPassword = useCallback((event) => {
      setConfirmPassword(event.target.value)
    },[setConfirmPassword])

  return(
    <div className="c-section-container">
      <h2 className="u-text__headline u-text-center">アカウント登録</h2>
      <div className="module-spacer--medium" />

      <TextInput
        fullWidth={true} label={"ユーザー名"} multiline={false}
        required={true} rows={1} value={username} type={"text"} onChange={inputUsername}
      />

      <TextInput
        fullWidth={true} label={"メールアドレス"} multiline={false}
        required={true} rows={1} value={email} type={"email"} onChange={inputEmail}
      />

      <TextInput
        fullWidth={true} label={"パスワード"} multiline={false}
        required={true} rows={1} value={password} type={"password"} onChange={inputPassword}
      />

      <TextInput
        fullWidth={true} label={"パスワード(確認用)"} multiline={false}
        required={true} rows={1} value={confirmPassword} type={"password"} onChange={inputConfirmPassword}
      />

      <div className="module-spacer--medium" />

      <div className="center">
        <PrimaryButton
          label={"アカウントを登録する"}
          onClick={() => console.log("Clicked!")}
        />
      </div>
    </div>
  )
}

export default SignUp
```

- テキスト入力フィールドを扱うときは、各入力値を受ける state を``useState ()``で定義し、``useCallback()``で永続化する（実践編#12のおさらい）
- 「アカウントを登録する」ボタンの onClick イベントには、本来はアカウント登録処理を行うイベントを埋め込むが、今はダミーで``console.log("Clicked!")``を設置

作成した SignUp に対応するルーティング(``/signup``)を定義します。

```react:src/Router.jsx
import React from 'react';
import {Route, Switch} from "react-router";
import {Home,Login,SignUp} from "./templates";

const Router = () => {
  return (
    <Switch>
      <Route exact path={"/signup"} component={SignUp} />
      <Route exact path={"/login"} component={Login} />
      <Route exact path={"(/)?"} component={Home} />
    </Switch>
  );
};

export default Router
```

これで、SignUp画面で一通りできたはずです。ブラウザで確認してみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/444f92b7-3d5e-73ae-6ab6-2961ae96dc13.png)

いい感じにできています！アカウント登録ボタンを押してみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/cc8de988-1948-cbbd-5b6e-4fbbc4930ec2.png)

``console.log("Clicked!")``が発火しています！

### アカウント登録機能を作る

今回講座の肝です。先ほどのアカウント登録ボタンの中身を作ります。

一般に、reducksパターンにおいては state に関わる処理を定義する場合、以下の4ファイルを新規作成or修正します

```:
1. コンポーネントファイル
2. operations.js
3. actions.js
4. reducers.js
```

``1. コンポーネントファイル``は、ここではまさに先ほど作成した``SignUp.jsx``です。

この中に、実装したい処理の発火点を作ります。今回のケースでは、 **アカウント登録処理を行う関数である signUp() を、「アカウントを登録する」ボタンの onClickイベントに埋め込む** ということになります。

``2. operations.js``で、実際に実行したい処理を記述します。今回のケースでは**引数のバリデーションを確認した上で、DB(Cloud firestore)にユーザー情報を登録する関数であるsignUp()を定義する**ということになります。

``3. actions.js``, ``4. reducers.js``で、``2. operations.js``で処理をされた値を元に Store内の state の変更を行います。

しかし今回のケースでは、「signUp()関数は user state に関わる処理のため reducks/users/operations.js に記述をするが、アカウント登録に使用したユーザー情報は画面描画には使用せず、登録後はすぐにルートにリダイレクトさせるため state を更新する必要はない」という状況のため、``3. actions.js``, ``4. reducers.js``についてはノータッチになります（こういうケースは珍しい方かと思います）

後に作るサインイン機能は、実際に state の更新まで行うので、上記流れで実装をしますので、そのときに改めて解説します。

まず、``1. コンポーネントファイル``に関数を設置しましょう。

```react:src/templates/SignUp.jsx
.
.
.
import {signUp} from "../reducks/users/operations"
import {useDispatch} from "react-redux"

const SignUp = () => {
  const dispatch = useDispatch()
  .
  .
  .

  return(
    <div className="c-section-container">
      .
      .
      .
      <div className="center">
        <PrimaryButton
          label={"アカウントを登録する"}
          onClick={() => dispatch(signUp(username,email,password,confirmPassword))}
        />
      </div>
    </div>
  )
}
```

アカウント登録を行う関数として定義される``signUp()``に対して、必要な引数を渡してonClickイベントに設置します。

この``signUp()``はこれから作成するもので、これは``operations.js``に記述します。

``operations.js``に定義した関数をコンポーネントで利用するためには、Hooksの一種である``useDispatch()``を使う必要があります。

次に、``operations.js``に``signUp()``を定義します。

```src/reducks/users/operations.js
.
.
.
import {push} from "connected-react-router";
import {auth, db, FirebaseTimestamp} from "../../firebase/index"
.
.
.
export const signUp = (username,email,password,confiramPassword) => {
  return async (dispatch) => {

    if (username === "" || email === "" || password === "" || confiramPassword === "") {
      alert("必須項目が未入力です")
      return false
    }

    if (password !== confiramPassword) {
      alert("パスワードが一致しません。もう一度お試しください")
      return false
    }

    return auth.createUserWithEmailAndPassword(email,password)
      .then(result => {
        const user = result.user

        if (user) {
          const uid = user.uid
          const timestamp = FirebaseTimestamp.now()

          const userInitialData = {
            created_at: timestamp,
            email: email,
            role: "customer",
            uid: uid,
            updated_at: timestamp,
            username: username
          }

          db.collection("users").doc(uid).set(userInitialData)
          .then(()=>{
            dispatch(push("/"))
          })
        }
      })
  }
}
```

- ``src/firebase/index.js``で定数化した``auth``,``db``,``FirebaseTimestamp``をインポート
- 引数に対するバリデーションを実施。空欄のものがあるときか、パスワードが不一致のときは``false``を返して処理を終了させる
- ``async``を入れて非同期処理を制御（DB通信時のお約束）
- ``auth.createUserWithEmailAndPassword()``で、メール／パスワード認証によるfirebase側のとの通信を簡単に実装できる
- ``uid``は``unique id``の略。``auth.createUserWithEmailAndPassword()``を実行した時点で自動的に生成される(resultの中に含まれる)
- usersコレクションのうち、上記の``uid``のところへ、各引数を保存する。保存が完了したら、ルートへリダイレクトする

これでsignUp()関数の設定は完了しました。しかし、もう一つ追加でやることがあります。

現時点では、Cloud Firestore へのデータの書き込みが禁止された設定になっているため、それをいったん解除する必要があります。

データの書き込み・読み込み権限は``firestore.rules``で定義します。

```:./firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {
      allow read: if request.auth.uid != null;
      allow create;
      allow update: if request.auth.uid == userId;
      allow delete: if request.auth.uid == userId;
    }
  }
}
```

usersコレクションに対して、createはいつでも可能、read、update, deleteはユーザー認証状態でのみ可能、と言う設定になっています。

これを有効化するため、``firestore.rules``のみをいったんfirebase側にデプロイする必要があります。

```terminal:terminal
$ firebase deploy --only firestore:rules
```

ここまでやれば準備完了です！ブラウザから実際にアカウント登録を実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/e70fdb12-5bbf-c7f2-0a0d-9e9d055ee9f0.png)

アカウント登録ボタンを押すと、ルートへリダイレクトされます。firebaseコンソールから、実際にアカウントが作成されたかを確認します。

#### Authentication
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/077d2112-9e57-5eea-b301-9bac02b67cd0.png)

#### Database
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/30d77a57-6f28-b400-b15f-85605e914bb3.png)

アカウント登録が完了していますね！

### Sign in 画面を作る

Sign Up画面をベースに、Sign in画面も作っていきます。修正するファイルは以下の通り。

繰り返しになりますが、reducksパターンにおいては、state に関わる処理を定義する場合、以下の4ファイルを新規作成or修正します。

```:
1. コンポーネントファイル
2. operations.js
3. actions.js
4. reducers.js
```

``1.コンポーネントファイル``として、``SignIn.jsx``を新たに作成します。この中に、``2. operations.js``で定義する``signIn()``関数を設置したボタンを配置します。

``2. operations.js``で、``signIn()``関数を追記します。この関数は、**引数として渡されたユーザー情報を元にDB(Cloud firebase)と通信をしてユーザー情報を取得し、それをactionsjsへ渡す**という役割を担います。

``3.actions.js``および``4. reducers.js``で、``2. operations.js``でDBから取ってきたユーザー情報を元に Store 内の state を更新する記述を行います。

今回は、すでにサインイン処理についてある程度記述がされています。``3.actions.js``についてのみ、一部修正をします。

以上の流れで実装を行います。実際に新規作成or修正するファイルをまとめると、

```:
1. src/templates/SignIn.jsx
2. src/reducks/users/operations.js
3. src/reducks/users/actions.js
4. src/reducks/store/initialState.js // actions.jsの修正に応じて一部修正
5. src/templates/index.js // エントリーポイント。SignIn.jsxを新規作成したため追記が必要。
6. src/Router.jsx // SignIn画面のルーティング。SignIn.jsxを新規作成したため追記が必要。
```

```react:src/templates/SignIn.jsx
import React, {useState,useCallback} from "react";
import {PrimaryButton,TextInput} from "../components/UIkit"
import {signIn} from "../reducks/users/operations"
import {useDispatch} from "react-redux"

const SignIn = () => {
  const dispatch = useDispatch()

  const [email,setEmail] = useState()
  const [password,setPassword] = useState()

    const inputEmail = useCallback((event) => {
      setEmail(event.target.value)
    },[setEmail])

    const inputPassword = useCallback((event) => {
      setPassword(event.target.value)
    },[setPassword])

  return(
    <div className="c-section-container">
      <h2 className="u-text__headline u-text-center">サインイン</h2>
      <div className="module-spacer--medium" />

      <TextInput
        fullWidth={true} label={"メールアドレス"} multiline={false}
        required={true} rows={1} value={email} type={"email"} onChange={inputEmail}
      />

      <TextInput
        fullWidth={true} label={"パスワード"} multiline={false}
        required={true} rows={1} value={password} type={"password"} onChange={inputPassword}
      />

      <div className="module-spacer--medium" />

      <div className="center">
        <PrimaryButton
          label={"サインイン"}
          onClick={() => dispatch(signIn(email,password))}
        />
      </div>
    </div>
  )
}

export default SignIn
```

- サインインに必要な情報は``email``と``password``のみなので、``username``と``confirmPassword``の入力フォーム（及びそれらを管理するためのstate）を削除
- サインインボタンを押すことで、operations.jsで定義する``signIn()``関数が発火するように記述


```javascript:src/reducks/users/operations.js
import { signInAction } from "./actions";
import {push} from "connected-react-router";
import {auth, db, FirebaseTimestamp} from "../../firebase/index"

export const signIn = (email,password) => {
  return async (dispatch) => {

    if (email === "" || password === "" ) {
      alert("必須項目が未入力です")
      return false
    }

    auth.signInWithEmailAndPassword(email,password)
      .then(result => {
        const user = result.user

        if (user) {
          const uid = user.uid

          db.collection("users").doc(uid).get()
            .then(snapshot => {
              const data = snapshot.data()

              dispatch(signInAction({
                isSignedIn: true,
                role: data.role,
                uid: uid,
                username: data.username
              }))

              dispatch(push("/"))
            })
        }
      })
  }
}

.
.
.
```

- ``auth.signInWithEmailAndPassword()``でサインイン認証が行える。
- 上記メソッドの返り値をもとに、DBから具体的なユーザー情報を取り出す。
- ユーザー情報を``signInAction``に渡すことで、stateの更新を行う。

```javascript:src/reducks/users/actions.js
export const SIGN_IN = "SIGN_IN";
export const signInAction = (userState) => {
  return {
    type: "SIGN_IN",
    payload: {
      isSignedIn: true,
      role: userState.role,
      uid: userState.uid,
      username: userState.username
    }
  }
};

.
.
.
```

- ``operations.js``より引数として渡されたユーザー情報を受け取るよう記述。
- ``role``カラムを追加している。

```javascript:src/reducks/users/reducers.js(変更箇所なし)
import * as Actions from './actions'
import initialState from '../store/initialState'

export const UsersReducer = (state = initialState.users, action) => {
  switch (action.type) {
    case Actions.SIGN_IN:
      return {
        ...state,
        ...action.payload
      };
    default:
      return state
  }
}
```

参考として掲載。先のアクションは``case Actions.SIGN_IN:``が対応しています。


```javascript:src/reducks/store/store.js(変更箇所なし)
.
.
.
export default function createStore(history) {
  return reduxCreateStore(
    combineReducers({
      router: connectRouter(history),
      users: UsersReducer
    }),
.
.
.
```

参考として掲載。``UsersReducer``からStoreに指令が来ることで、サインイン時のユーザー情報をもとにstateが更新されます。

```javascript:src/reducks/store/initialState.js
const initialState = {
  users: {
    isSignedIn: false,
    role:  "",
    uid: "",
    username: ""
  }
};

export default initialState

```

- ``role``カラムを追加している。


```javascript:src/templates/index.js
export {default as Home} from './Home'
export {default as SignIn} from './SignIn'
export {default as SignUp} from './SignUp'
```
- Loginを削除し、SignInを追加。


```react:src/Router.jsx
import React from 'react';
import {Route, Switch} from "react-router";
import {Home,SignIn,SignUp} from "./templates";

const Router = () => {
  return (
    <Switch>
      <Route exact path={"/signup"} component={SignUp} />
      <Route exact path={"/signin"} component={SignIn} />
      <Route exact path={"(/)?"} component={Home} />
    </Switch>
  );
};

export default Router
```

- SignInテンプレートに対するルーティング(/signin)を追加。

これにより、サインイン画面が完成したはずです。ブラウザで、まずルート(/)を見てみます。

#### ``localhost:3000/``

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/018aa6f9-5b03-d1e6-bf50-1382bf4d25f4.png)

initialStateで定義した通り、ユーザーID、ユーザー名がブランクの状態です。

サインイン画面(/signin)より、先ほどアカウント登録したユーザー情報を用いてサインインを行います。

#### ``localhost:3000/signin``

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/25078d2d-5b91-cefb-97d7-5d24e5ecdbe6.png)

サインインボタンを押すと、ルートへリダイレクトされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/b06c9d07-5e21-08ff-0067-d7892ec8297a.png)

先ほど登録したアカウントのユーザーID,ユーザー名が表示されていればOKです！

## おわり
再度要点をまとめると、

1. ``店舗:ユーザー ＝ 1:n`` のECアプリを開発する
2. ``firebase.auth``による新規登録、サインイン認証を実装する
3. ``reducksパターン``に沿った関数の実装手順を理解する

以上です！次回は認証のリッスンによる state の永続化を行います。