React Hooks + Rails APIを使ったログイン・認証系機能の実装手順

## はじめに
Rails(API)とReact Hooksを使って`新規登録／ログイン／ログアウト`機能を実装してみました。

Rails側ではDeviseなどのライブラリは使わずに`セッション`を使って実装しています。
セッションメソッドなどについて知る際に役に立つ公式ドキュメント：[Railsガイド（セッション）](https://railsguides.jp/security.html#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8)

参考にさせて頂いた資料は[React + Rails API Authentication](https://www.youtube.com/playlist?list=PLgYiyoyNPrv_yNp5Pzsx0A3gQ8-tfg66j)という[edutechional](https://www.youtube.com/channel/UCgGf1eq52dPTVuf1Njb57Hw)さんの動画です。（こちらの動画ではReact側はクラスコンポーネントを用いた方法を解説してくださっていました。）


## 環境

__RubyやRailsや、Reactアプリ作成に必要なyarnまたはnpmのインストールなどの環境構築は省きます。__

__Rails側__
　・Ruby 2.6
　・Rails 5.2
　・bcrypt
　・rack-cors

__React側__
　・create-react-app
　・axios
　・react-router-dom
　__※React HooksはReact16.8以降でしか使えないので注意です__

## 実装手順

__1.Rails側の実装__
　・Railsアプリケーションのセットアップ
　・CORSの設定
　・cookiesを正しく処理するための設定
　・Pumaの設定
　・Userモデルの作成とbcryptの設定
　・ルーティングの設定
　・コントローラの作成とその他の設定

__2.React側の実装__
　・Reactアプリケーションのセットアップ
　・react-router-domでルーティングを設定する
　・新規登録コンポーネントを実装する
　・ログイン状態をチェックする機能を実装する
　・ログインコンポーネントを実装する
　・ログイン状態を維持する機能を実装する
　・ログアウト機能を実装する

## 1.Rails側の実装
### 1-1.Railsアプリケーションのセットアップ

* __Rails newコマンドを実行する。DBはPostgreSQLを使います。__

```
$ rails new auth-app-api --database=postgresql
```

* __rails db:createコマンドを実行し、DBを立ち上げる。__

```
$ rails db:create
```

* __Gemfileにbcryptとrack-corsを追加し、インストールする。__

```ruby:Gemfile
gem 'bcrypt'

gem 'rack-cors'
```
> rack-corsは、CORSの設定を行うために用います。CORSについては後述します。

```
$ bundle
```


<br>

### CORS（Cross-Origin Resource Sharing）の設定

通常、JavaScript Webアプリ（1つのオリジン）がRails API（別のオリジン）のリソースにアクセスすること（GET, POST, PUT, DELETE などのHTTPリクエスト）は制限されています。

なので僕らのReactアプリが、Rails APIにリクエストを送信できるようにするためにCORSを有効にする必要があります。

CORSとはなんぞやと言う方は以下の記事をまず眺めてみてから、「CORS」で色々とググってみると良いかなと思います。
[オリジン間リソース共有 (CORS) - MDN web docs](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)

そんなCORSの設定を以下で行います。

* __まずは`config/initializers`ディレクトリ内に、 新たに`cors.rb`ファイルを作成し、以下のようにコードを記述します。__

```ruby:config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
    allow do
        origins 'http://localhost:3000'

        resource '*',
            headers: :any,
            methods: [:get, :post, :put, :patch, :delete, :options, :head],
            credentials: true
    end

    #本番環境用のオリジン設定
    allow do
        origins 'https:<自身が設定するアプリのURL>'

        resource '*',
            headers: :any,
            methods: [:get, :post, :put, :patch, :delete, :options, :head],
            credentials: true
    end
end
```

>上記のコードをざっくり説明しますと、`allow ... doに囲まれたオリジンからの色んなリクエストを許可する`みたいなことです。

>`origins`でフロントエンド側の`オリジン（URL）`を指定し、
`resource`で`「許可するヘッダ」``「許可するHTTPリクエスト」``「Cookie保持を許可するかどうか」`を指定します。

>この設定により、localhost:3000オリジン（Reactアプリ側）からの全てのタイプのヘッダやリクエストの許可や、Cookieの保持がRails側で許可されるようになりました。

<br>

### Cookieを正しく処理するための設定

* __`config/initializers/session_store.rb`ファイルを作成し、サーバーがHTTP Cookieを処理できるように設定を行う。__

```ruby:config/initializers/session_store.rb
if Rails.env === 'production'
    Rails.application.config.session_store :cookie_store, key: '_auth-app-api', domain: 'フロントエンドのドメイン'
else
    Rails.application.config.session_store :cookie_store, key: '_auth-app-api'
end
```

> サーバーはCookieについて、少なくともkeyについて知らせる必要があります。（keyはアンダースコアから始めることに注意です。）

>keyを指定すると、ブラウザ側には「Set-Cookieヘッダ」として、以下のような形でCookieが保持されることになります。

>```ruby:Set-Cookie
_auth-app-api=59gajghl89jgaji5l.......
```

> domainについては本番環境へ移行する際に必要になります。


<br>

### Pumaの設定

* __`config/puma.rb`ファイルを開いて、デフォルトポートを3001に変更する。__

```ruby:config/puma.rb
-  port        ENV.fetch("PORT") { 3000 }
+  port        ENV.fetch("PORT") { 3001 }
```

> 上記の設定により、Railsのローカルサーバーが3001番ポートで開くようになります。 

>これを設定することにより、バックエンドとフロントエンドが同じポート番号でサーバーを開こうとするのを防ぐことができます。（デフォルトではReactもRailsも3000で開くので要設定）

<br>

### Userモデルの作成とbcryptの設定

* __Userモデルを作成し、内容をデータベースへ保存。__

```
$ rails g model User email password_digest
$ rails db:migrate
```

> passwordは`必ずpassword_digest`としてください。


* __`user.rb`を開き、Userモデルとbcryptの設定をします。__

```ruby:app/models/user.rb
class User < ApplicationRecord
    has_secure_password

    validates :email, presence: true
    validates :email, uniqueness: true
    validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
end
```

> has_secure_passwordを記述することにより、新規登録時`password`と`password_confirmation`の2つを入力することで、`password_digest`として値が変換されDBに保存されるので、ユーザーデータがセキュアな物になります。

<br>

### ルーティング設定

* __`config/routes.rb`にルーティングの設定を行います。__

```ruby:config/routes.rb
Rails.application.routes.draw do

  post '/signup', to: 'registrations#signup'

  post '/login', to: 'sessions#login'
  delete '/logout', to: 'sessions#logout'
  get '/logged_in', to: 'sessions#logged_in?'

end
```

> `/signup`では、Registrationsコントローラのcreateアクションへ、POSTリクエストを送信し、`新規登録`することになります。

> `/login`では、SessionsコントローラのcreateアクションへPOSTリクエストを送信し、`ログイン`することになります。`/logout`もこれと同様の動きです。

> `/logged_in`では、React側で`ユーザーのログイン状態を追跡`するために必要なルーティングです。詳しくはReactでの機能実装時に説明します。

<br>

### コントローラの作成

* __`controllers/registrations_controller.rb`を作成し、以下のようにコードを記述。__

```ruby:app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController

    def signup
        @user = User.new(registrations_params)
        
        if @user.save
            login!
            render json: { status: :created, user: @user }
        else
            render json: { status: 500 }
        end
    end

    private

        def registrations_params
            params.require(:user).permit(:email, :password, :password_confirmation)
        end

end
```

> ここでは、React側からPOST送信されるユーザーデータをDBに保存し、その後JSONとして返り値をレンダリングしています。

> `login!`は、セッションを使ってユーザーをログインさせるヘルパーメソッドで、後で定義します。

> React側では、ここでレンダリングされたJSONデータを取得して扱います。

> それから、ここでは新規登録の処理になるので、`registrations_params`のパラメータにてpasswordと共にpassword_confirmationを渡していることに注意してください。

* __`controllers/sessions_controller.rb`ファイルを作成し、以下のようにコードを記述する。__

```ruby:app/controllers/sessions_controllers.rb
class SessionsController < ApplicationController
    def login
        @user = User.find_by(email: session_params[:email])

        if @user && @user.authenticate(session_params[:password])
            login!
            render json: { logged_in: true, user: @user }
        else
            render json: { status: 401, errors: ['認証に失敗しました。', '正しいメールアドレス・パスワードを入力し直すか、新規登録を行ってください。'] }
        end
    end

    def logout
        reset_session
        render json: { status: 200, logged_out: true }
    end

    def logged_in?
        if @current_user
            render json: { logged_in: true, user: current_user }
        else
            render json: { logged_in: false, message: 'ユーザーが存在しません' }
        end
    end

    private

        def session_params
            params.require(:user).permit(:username, :email, :password)
        end

end
```

> `loginアクション`：ログインの処理を行います。if文の部分では、実際にユーザーが入力したpassword・password_confirmationの値と、DBに保存されているpassword_digestの値を比較し、合致した場合にtrueを返します。

> `logoutアクション`：ログアウトの処理を行います。
　　
>`logged_in?アクション`：ユーザーのログイン状態をReactへ返します。@current_userはログイン中のユーザーを表すインスタンス変数で、このログイン中のユーザーが存在していれば、それに応じて適切な値を返します。
　　
> JSON内の値がどのように使うのかは、React側で機能を実装する際にその都度説明します。

* __`controllers/application_controller.rb`に、必要事項を記述していく。__

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base

    skip_before_action :verify_authenticity_token

    helper_method :login!, :current_user

    def login!
        session[:user_id] = @user.id
    end

    def current_user
        @current_user ||= User.find(session[:user_id]) if session[:user_id]
    end

end
```

>`skip_before_action :verify_authenticity_token`：Railsが認証トークンを使用しないようにする。認証トークンというのは、CSRF攻撃を防ぐために、Railsフォームからコントローラへと送信されるパラメータの中に追加されるセキュリティートークンのことですが、ここでは使用しないのでスキップさせます。
　　
> `login!`：セッションを使ってユーザーをログインさせます。
　　
> `@current_user`：ログイン中のユーザーを取得するインスタンス変数を定義しています。

<br>

## 2.React側の実装

※`React Hooks`を使用するため、コンポーネントは`関数コンポーネント`で構築し、初期値の設定は`useState()`フックを使用し、レンダリング後のマウント処理は`useEffect()`フックを使用します。

### Reactアプリケーションのセットアップ

##### 1.`create-react-app`を使ってReactアプリケーションを構築する

```
$ npx create-react-app auth-app-client && cd auth-app-client
```

##### 2.アプリケーションが構築されたら、不要なファイルを削除する。
　　→App.css、App.test.js、logo.svg

##### 3.`src/App.js`コンポーネントの状態を、一旦以下のようにすっきりさせる。

```react:src/App.js
import React from 'react';

export default function App() {
  render() {
    return (
      <div>
        
      </div>
    )
  }
}
```

　　・デフォルトではクラスコンポーネントで構築されていると思うので、`関数コンポーネント`に書き換えておきます。
　　・`React Hooks`は関数コンポーネントでしか使用できないので、クラスコンポーネントで実装してしまわないように注意してください。

##### 4.`srx/index.js`ファイルの中身も、以下のようにすっきりとさせる。

```react:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render( <App />, document.getElementById('root') );
```

##### 5.必要な依存パッケージを`yarn add`コマンドでインストールする。

```
$ yarn add axios react-router-dom
```

　　・`axios`：APIとの非同期通信を行うのに必要なパッケージ。
　　・`react-router-dom`：Reactアプリ内でのルーティング設定に必要なパッケージ。

<br>

### react-router-domでルーティングを設定する

それぞれのコンポーネントへと導くルータの役割を`App.js`に担ってもらうことにします。
そのApp.jsに、今から作成する`Home.jsコンポーネント`、`Dashboard.jsコンポーネント`へのルーティングを作成していきます。

##### 1.`Home.js`コンポーネントを作成する。

```react:src/components/Home.js
import React from 'react'

export default function Home() {
    return (
        <div>
            <h1>Home</h1>
        </div>
    )
}
```

　　・`src/components`ディレクトリを新たに作成し、そこに`Home.js`ファイルを作成する。
　　・このHome.jsコンポーネントを`ルートURL`("localhost:3000")とし、Home.jsコンポーネントに認証系機能のフォームなどをやログインフォームなどを作成していきます。


##### 2.`Dashboard.js`コンポーネントを作成する。

```react/src/components/Dashboard.js
import React from 'react'

export default function Dashboard() {
    return (
        <div>
            <h1>Dashboard</h1>
        </div>
    )
}
```

　　・`src/components`に、`Dashboard.js`ファイルを作成します。
　　・Dashboard.jsコンポーネントの役割は、ログイン処理が完了した後にリダイレクトされるページとしてのみ使用します。

##### 3.`App.js`に、Home.jsコンポーネントとDashboard.jsコンポーネントへのルーティングを作成する。

```react:src/App.js
import React from 'react'
import { BrowserRouter, Switch, Route } from 'react-router-dom'

import Home from './components/Home'
import Dashboard from './components/Dashboard'

export default function App() {
  return (
    <div>
      <BrowserRouter>
        <Switch>
          <Route exact path={"/"} component={Home} />
          <Route exact path={"/dashboard"} component={Dashboard} />
        </Switch>
      </BrowserRouter>
    </div>
  )
}
```

　　・`import { BrowserRouter, Switch, Route } from 'react-router-dom'`：react-router-domからルーティングの設定に必要なコンポーネントをimport。ルーターはルートのコンポーネントで使用すること。

　　・次にHome.js、Dashboard.jsコンポーネントをimport。

　　・`<Route exact path={"/"} component={Home} />`：`Routeコンポーネント`を使ってHomeコンポーネントへのルーティングを設定。pathは`"/"`。Dashboardコンポーネントへのルーティングpathは`"/dashboard"`とします。

　　・ルーターは、RouteコンポーネントをSwitchコンポーネントでラッピングし、それらをさらにBrowserRouteコンポーネントでラップすることで動作する。

　　__・react-router-dom公式：https://reactrouter.com/web/guides/quick-start__


_ここまでの状態で`$ yarn start`コマンドを実行しlocalhost:3000へアクセスしてみると、Home.jsコンポーネントがレンダリングされていることが確認でき、URLに/dashboardを付け加えるとDashboard.jsコンポーネントがレンダリングされることが確認できます。_

<br>

### 新規登録コンポーネントを実装する

新規登録コンポーネントでは、ユーザーの新規登録機能の実装と、新規登録と同時にユーザーをログインさせる機能を実装します。

##### 1.`Registration.js`コンポーネントを作成する。

```react:src/components/auth/Registrations.js
import React from 'react'

export default function Registration() {
    return (
        <div>
           <p>新規登録</p>
        </div>
    )
}
```

　　・src/componentsディレクトリ配下に、新たに`auth`ディレクトリを作成し、`auth`内に`Registration.js`を作成する。
　　・まずはRegistrationコンポーネントが、正常にレンダリングされるかどうかを確認するので、とりあえず記述はこれだけにしておきます。

##### 2.`Registration.js`コンポーネントを、`Home.js`でレンダリングする。

```react:src/components/Home.js
import React from 'react'
import Registration from './auth/Registration'

export default function Home() {
    return (
        <div>
            <h1>Home</h1>
            <Registration  />
        </div>
    )
}
```

　　・新規登録フォームはHomeページで表示させたいので、Registrationコンポーネントをレンダリングさせます。
　　・ブラウザで以下のように表示されていれば、正しくレンダリングが動作しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/89f014ff-6ee8-f92e-488e-48f1abfc4666.png)

##### 3.Registrationコンポーネントに`ユーザーオブジェクトの初期値`を定義する。

```react:src/components/auth/Registration.js
import React from 'react'
// useStateフックをimportする
import React, { useState } from 'react'

export default function Registration() {
    // useState()を用いて、ユーザーデータの初期値（空の文字列）を定義する。
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")
    const [passwordConfirmation, setPasswordConfirmation] = useState("")

    return (
        <div>
           <p>新規登録</p>
        </div>
    )
}
```

　　・`useState()`フックは、[引数1, 引数2]のように`配列`で初期値を定義する。`引数１には変数`を定義し、`引数２には関数`を定義します。`ユーザーデータを参照する場合は引数１の変数の方`を使用し、`ユーザーデータを書き換えたい場合は引数２の関数の方`を使用します。

##### 4.Registrationコンポーネントに`新規登録フォーム`を作成する。

```react:src/components/auth/Registration.js
import React from 'react'
import React, { useState } from 'react'

export default function Registration() {
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")
    const [passwordConfirmation, setPasswordConfirmation] = useState("")

    return (
        <div>
           <p>新規登録</p>

           {/* 追加 */}
            <form>
                <input
                    type="email"
                    name="email"
                    placeholder="メールアドレス"
                    value={email}
                />
                <input
                    type="password"
                    name="password"
                    placeholder="パスワード"
                    value={password}
                />
                <input
                    type="password"
                    name="password_confirmation"
                    placeholder="確認用パスワード"
                    value={passwordConfirmation}
                />

                <button type="submit">登録</button>
            </form>
        </div>
    )
}
```

　　・ここでは、先ほどuseState()で定義した初期値を使っています。先ほども述べた通り、`データの参照は第1引数の変数`で行いますので、valueにはユーザーデータの変数を渡しています。

##### 5.`onSubmit`イベントと、`onChange`イベントの定義。

```react:src/components/auth/Registration.js
import React from 'react'
import React, { useState } from 'react'

export default function Registration() {
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")
    const [passwordConfirmation, setPasswordConfirmation] = useState("")

    const handleSubmit = (event) => {
        console.log("イベント発火")
        event.preventDefault()
    }

    return (
        <div>
           <p>新規登録</p>

           {/* onSubmit、onChangeイベントを追加 */}
            <form onSubmit={handleSubmit}>
                <input
                    type="email"
                    name="email"
                    placeholder="メールアドレス"
                    value={email}
                    onChange={event => setEmail(event.target.value)}
                />
                <input
                    type="password"
                    name="password"
                    placeholder="パスワード"
                    value={password}
                    onChange={event => setPassword(event.target.value)}
                />
                <input
                    type="password"
                    name="password_confirmation"
                    placeholder="確認用パスワード"
                    value={passwordConfirmation}
                    onChange={event => setPasswordConfirmation(event.target.value)}
                />

                <button type="submit">登録</button>
            </form>
        </div>
    )
}
```

　　
　　・`onSubmit`には、あらかじめ`handleSubmitイベントハンドラ`を定義し、それを渡しています。
　　
　　・handleSubmitは`「登録」ボタンがクリックされた時に発火`します。ここではイベントハンドラの動作を確認するために、処理を一旦console.log()としていますが、実際には`Rails側にデータを送信する処理`をここに記述します。`event.preventDefault()`ではフォームの値を初期化します。クリック後も値が残り続けてしまうので。

　　・`onChangeは`、フォームを入力している`最中`に発火し、入力した値でユーザーデータを書き換えます。
　　　例えば、emailフォームに文字列を入力→`setEmail関数`により`email変数`の値がその文字列に書き換わる→書き換わった`email変数`の値がvalueに渡される。onChangeイベントはこのサイクルを繰り返します。
　　・onChangeでは、関数を定義していません。関数を定義するよりも直接値を処理する記述を行う方が楽なのでこうしてます。
　
　　・ブラウザで、ボタンをクリックしてコンソールにconsole.log()の値が表示されていれば正常の動作しています。

##### 6.次は、`axios`を使って、フォームに入力したユーザーデータを`Rails側に送信`する機能を実装します。

```react:src/components/auth/Registration.js
...

// 追加
import axios from 'axios'

export default function Registration() {
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")
    const [passwordConfirmation, setPasswordConfirmation] = useState("")

    const handleSubmit = (event) => {

        //追加
        axios.post("http://localhost:3001/signup",
            {
                user: {
                    email: email,
                    password: password,
                    password_confirmation: passwordConfirmation
                }
            },
            { withCredentials: true }
        ).then(response => {
            console.log("registration res", response)
        }).catch(error => {
            console.log("registration error", error)
        })
        event.preventDefault()

    }

    return (
        <div>
           <p>新規登録</p>

            <form onSubmit={handleSubmit}>
                <input
                    type="email"
                    name="email"
                    placeholder="メールアドレス"
                    value={email}
                    onChange={event => setEmail(event.target.value)}
                />
                <input
                    type="password"
                    name="password"
                    placeholder="パスワード"
                    value={password}
                    onChange={event => setPassword(event.target.value)}
                />
                <input
                    type="password"
                    name="password_confirmation"
                    placeholder="確認用パスワード"
                    value={passwordConfirmation}
                    onChange={event => setPasswordConfirmation(event.target.value)}
                />

                <button type="submit">登録</button>
            </form>
        </div>
    )
}
```

　　・`axios.post(第１引数, 第２引数, 第３引数)`：第１引数にはデータを送信したいRails側のURL、第２引数にはユーザーオブジェクト、第３引数にはwithCredentials: trueという値を渡してます。

　　・第１引数のURLには`"/signup"`を指定していますので、`Railsの"/signup"`ルーティング→`Registrationsコントローラ`→`signupアクション`へとデータが渡されて行きます。

　　・Raileへ渡すデータは、上記の通り`第２引数のユーザーオブジェクト`です。渡している`email`、`password`、`password_confirmation`は、`useState()フック`で定義されている変数のことです。先ほども言った通り、データを参照する場合は変数の方を使用します。

　　・`withCredentials: true(必須)`の部分では、`axios`でバックエンドのAPI(Rails)と通信する際にデータに`cookie`を含めるかどうかを決める物で、`true`にすることで含めることができ、今回の認証機能にはこのcookieを使用するので、trueとしてください

　　・さて、ここで一旦Rails側で定義した`コントローラ`を見てみます。これを見ればわかる通り、React側で送信するユーザーオブジェクトは、`registration_paramsへ渡されます`。そしてそのデータを基に、ユーザーが新たに`作成・保存`されます。
![スクリーンショット 2020-08-26 20.07.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/60e2c109-765b-591d-623e-a3817b0d4e73.png)

　　・次に、`then`の部分です。これは`コントローラがユーザーの作成に成功した場合`に、引数で`Railsから戻り値（その他様々なデータ）を取得`し、その戻り値を使って処理を行います。実際にはここに`React側でユーザーをログインさせる`処理を記述していきますが、一旦console.log()を記述し、Railsから値が返ってくるかどうかをテストします。

　　・`catch`部分では、`ユーザーの作成に失敗した場`合に、Railsからの戻り値としてエラーを取得します。

　　・Railsからの戻り値がどういうものかは、上記のRegistrationsコントローラの画像で確認できますが。どちらも`JSON`としてデータが返って来ます。`成功`した場合は`{status: created, user: @user}`という値が、`失敗`した場合には`{status: 500}`が返って来ます。

　　・以下の画像は、then部分で記述した`console.log("registration res", response)`の中身です。ユーザーの登録が成功した場合には以下の値が返って来ます。
　　　特に大事な部分は、`data:{ status:..., user: {...} }`の部分で、このデータををReact側で扱うことになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/de15b712-1c7f-c665-cc57-a596667d38e2.png)

<br>

### 新規登録コンポーネントまとめ
　1.`Registration`コンポーネントを作成する。
　2.HomeコンポーネントにRegistrationコンポーネントをレンダリングさせる。
　3.Registrationコンポーネントに、`useState()`フックをimportし、初期値を定義する。
　4.Registrationコンポーネントに`新規登録フォーム`を作成する。
　5.`onSubmit`イベント、`onChange`イベントを定義(仮)する。
　6.`axios`を使って、`handleSubmitイベントハンドラ`内に、ユーザーデータをRails側に送信する機能を実装する。
　※handleSubmit内にログイン処理を書く前に、ログイン状態をチェックする機能を先に実装します。

<br>

### ログイン状態をチェックする機能を実装する

##### 1.`App.jsコンポーネント`に、ログイン状態をチェックするのに必要な`初期値`を定義する。

```react:src/App.js
// 追加する
import React, { useState } from 'react'
import { BrowserRouter, Switch, Route } from 'react-router-dom'

import Home from './components/Home'
import Dashboard from './components/Dashboard'

export default function App() {
  // 追加する
  const [loggedInStatus, setLoggedInStatus] = useState("未ログイン")
  const [user, setUser] = useState({})

  return (
    <div>
      <BrowserRouter>
        <Switch>
          <Route exact path={"/"} component={Home} />
          <Route exact path={"/dashboard"} component={Dashboard} />
        </Switch>
      </BrowserRouter>
    </div>
  )
}
```

　　・ログイン状態のチェックは、`複数のコンポーネントで使用したい`ので、全てのコンポーネントの`親`であるApp.jsに定義し、他のどのコンポーネントでも共有できるようにします。

　　・初期値の定義なのでまずはuseStateフックをimportします。

　　・`loggedInStatusオブジェクト`と`userオブジェクト`の初期値を定義しました。

　　・`loggedInStatus変数`では、`ユーザーのログイン状態を参照`します。デフォルトではログインしていない状態を表す`"未ログイン"`としています。

　　・userオブジェクトは、実際にユーザーをログインさせる際に必要になります。

##### 2.`Render Props`という手法を用いて、`App.js(親)からHome.js(子)へ`必要な値をpropsとして渡す。

```react:src/App.js
...

export default function App() {
  const [loggedInStatus, setLoggedInStatus] = useState("未ログイン")
  const [user, setUser] = useState({})

  return (

    <BrowserRouter>
      ...

          {/* 追加する */}
          <Route
            exact path={"/"}
            render={props => (
              <Home { ...props } loggedInStatus={loggedInStatus} />
            )}
          />

      ...
    <BrowserRouter />

  )
}
```

　　・Render Propsを用いるとそのコンポーネント記述したコードを、別のコンポーネントに引き継ぐことができます。

　　・`render={ props => ( <コンポーネント { ...props } /> ) }`とすることで、当該コンポーネントで定義している内容を、指定したコンポーネントへpropsとして渡すことができます。

　　・ちなみに、ここで渡しているpropsの中身を確認してみると、以下のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/3e936b75-1288-e73c-1c16-8c332ce01003.png)


　　・{ ...props }という書き方については、https://stackoverflow.com/questions/28452358/what-is-the-meaning-of-this-props-in-reactjs で分かり易く解説してくれています。

　　・そしてここには`loggedInStatus={loggedInStatus}`という記述がありますが、これは`Home.js内のloggedInStatus`という変数に、`App.jsのloggedInStatus変数を代入`していることを表します。この変数をHome.jsにてレンダリングしてあげることで、`Home.jsでログイン状態を表示`させることが可能になります。
　　　
　　（・正直ここに関してはHooksなりの良い書き方があるとは思うのですが、Hooks初心者なので今回はこれで妥協しました…。）

##### 3.`Home.js`で、ユーザーのログイン状態を表示する。

```react:src/components/Home.js
import React from 'react'
import Registration from './auth/Registration'

// propsを引数に書き加える
export default function Home(props) {

    return (
        <div>
            <h1>Home</h1>

            {/* 追加する */}
            <h2>ログイン状態: {props.loggedInStatus}</h2>
            <Registration  />
        </div>
    )
}
```

　　・関数コンポーネント自体に引数としてpropsを渡します、渡し忘れに注意。
　　・取得した`propsから`、先ほどApp.jsで渡した`loggedInStatus変数を取り出して表示`させます。
　　・ブラウザで正常に表示されていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/247b3c5c-012b-a0b8-04b3-1cb6857aa897.png)

##### 4.`Dashboard.js`にも同様に、Render Propsを用いてログイン状態を表示させる。


```react:src/App.js
return (

    <BrowserRouter>
      ...

          {/* 追加する */}
          <Route
            exact path={"/"}
            render={props => (
              <Dashboard { ...props } loggedInStatus={loggedInStatus} />
            )}
          />

      ...
    <BrowserRouter />

  )
```

```react:src/components/Dashboard.js
import React from 'react'

export default function Dashboard(props) {
    return (
        <div>
            <h1>Dashboard</h1>
            <h2>ログイン状態: {props.loggedInStatus}</h2>
        </div>
    )
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/cf90b172-58d2-3a1e-8dea-1481be068aa2.png)

### ログイン状態をチェックする機能まとめ
　1.`App.jsコンポーネント`に、ログイン状態をチェックするのに必要な`初期値`を定義する。
　2.`Render Props`という手法を用いて、`App.js(親)からHome.js(子)へ`必要な値をpropsとして渡す。
　3.`Home.js`で、ユーザーのログイン状態を表示する。
　4.`Dashboard.js`にも同様に、Render Propsを用いてログイン状態を表示させる。

<br>

### ログイン機能を実装する

ログイン状態を確認できる機能が実装できたので、次は実際にユーザーがログインできる機能を実装します。
_※Registration.jsコンポーネントのhandleSubmitイベントの機能が途中で終わっているので、その続きとしてログイン機能を実装します。_

##### 1.`Registration.js`のhandleSubmitイベント内のthen()の中に、ユーザーをログインさせるための記述を行う（仮）

```react:src/components/auth/Registration.js
import React, { useState } from 'react'
import axios from 'axios'

// 引数にpropsを渡す
export default function Registration(props) {

    ...

    const handleSubmit = (event) => {
        axios.post("http://localhost:3001/signup",
            {
                user: {
                    email: email,
                    password: password,
                    password_confirmation: passwordConfirmation
                }
            },
            { withCredentials: true }
        ).then(response => {

            // 追加
            if (response.data.status === 'created') {
                props.handleSuccessfulAuthentication(response.data)
            }

        }).catch(error => {
            console.log("registration error", error)
        })


        event.preventDefault()
    }

    return (
       ...
    )
}
```

　　・`if文の部分`：以下の画像は先ほど見たのと同様、新規登録ボタンを押した際に`Railsから返ってくるデータ`だが、ユーザーの登録に`成功`すると、`response.data.status`に`"created"`というステータスが返ってくる（そうRails側で設定したので）。なので、ステータスが"created"となったら正しいイベントが発火するように条件式を定義します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/15e4e906-b50f-46d1-5237-514d12f9375d.png)

　　・条件式内の処理では、`propsからhandleSuccessfulAuthentication()イベントハンドラを取得`し、そこに`responseで受け取ったデータのdataフィールド`を渡しています。

　　・見たらわかる通りイベントハンドラはpropsから取り出していますので、`イベントハンドラは別のコンポーネントで定義`し、それを`Registrationコンポーネントへ渡している`ことがわかります。

　　・このイベントハンドラこそまさに、ユーザーをログインさせる処理を実行するイベントハンドラになります。これは今記述を行っている新規登録フォームでも使いますが、この後作成するログインフォームでも使用することになります。つまり2度使うということなので、親コンポーネントにて関数化してそれを再利用しようということです。

##### 2.`Home.js`に、`handleSuccessfulAuthentication()イベントハンドラ`を定義する。

```react:src/components/Home.js
import React from 'react'
import Registration from './auth/Registration'

export default function Home(props) {

    // 追加
    const handleSuccessfulAuthentication = (data) => {
        props.history.push("/dashboard")
    }

    return (
        <div>
            <h1>Home</h1>
            <h2>ログイン状態: {props.loggedInStatus}</h2>

            {/* 書き加え */}
            <Registration handleSuccessfulAuthentication={handleSuccessfulAuthentication}
 />
        </div>
    )
}
```


　　・`handleSuccessfulAuthentication`では、引数を渡してますが、これは`ユーザーオブジェクト`のことです。処理として`props.history.push("/dashboard")`となっていますが、これは`"/dashboard"へ画面遷移させる処理`です。手始めに画面遷移するかどうかをチェックし、このイベントハンドラが正常に動作するかどうかをテストします。

　　・「ログイン状態をチェックするの項目2」で、Homeに渡されるpropsの画像を上の方で見せましたが、その中に`"history"というフィールド`があり、`"/"`URLでした。そこに`"/dashboard"`を`push`することで画面遷移を実現しています。

　　・「登録」ボタンを押してみて、以下のように遷移すればイベントハンドラは正常に動作しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/3eee7355-ec56-0fce-91b1-20a5171e3c79.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/a353e70d-719d-cf7d-1983-031cd5edfbea.png)

##### 3.`handleLogin()イベントハンドラ`を、handleSuccessfulAuthenticationイベントハンドラ内に記述する。

```react:src/components/Home.js
const handleSuccessfulAuthentication = (data) => {
        props.handleLogin(data)
        props.history.push("/dashboard")
}
```

　　・見てわかる通り`handleLogin()`イベントハンドラは、`props`から取り出しています。ということは親コンポーネントにて関数化して、それをここで再利用するということです。handleLogin()イベントハンドラはこの後App.jsにて定義します。

##### 4.`App.js`に、`handleLogin`イベントハンドラを定義する。

```react:src/App.js
export default function App() {
  const [loggedInStatus, setLoggedInStatus] = useState("未ログイン")
  const [user, setUser] = useState({})

  // 追加する
  const handleLogin = (data) => {
    setLoggedInStatus("ログインなう")
    setUser(data.user)
  }

  return (
     ...

          <Route
            exact
            path={"/"}
            render={props => (
              // 追加する
              <Home { ...props } handleLogin={handleLogin} loggedInStatus={loggedInStatus} />
            )}
          />

     ...
  )
}
```

　　・`handleLogin`イベントハンドラでは、`setLoggedInStatus()関数`を用いて、loggedInStatusオブジェクトの値を書き換えます。次に、引数で取得した`data`から`userフィールド`を取得し、`setUser()関数`に渡すことで、userオブジェクトの値を書き換えています。

　　・`Homeコンポーネント`でhandleLoginイベントハンドラが使用できるように、`propsとして`HomeコンポーネントにhandleLoginイベントハンドラを渡しています。

<br>

### ログイン機能を実装するまとめ
　1.`Registration.js`のhandleSubmitイベント内のthen()の中に、ユーザーをログインさせるための記述を行う（仮）
　2.`Home.js`に、`handleSuccessfulAuthentication()イベントハンドラ`を定義する。
　3.`handleLogin()イベントハンドラ`を、handleSuccessfulAuthenticationイベントハンドラ内に記述する。
　4.`App.js`に、`handleLogin`イベントハンドラを定義する。

<br>

__ここまで実装できたことで、React側でユーザーのログイン状態にすることができるようになりました。ブラウザでユーザーデータを入力し、新規登録を行ってみて、以下のように`画面遷移`に加え、`ログイン状態が変更`されれば正常に動作しています。__
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/30fc8419-917c-af23-280d-a9de5bc6335a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/aa167967-e05c-bc65-c6ff-aa6601d7c0a0.png)


__しかし、`cookie`を保持していないため、ブラウザを更新するとログイン状態はすぐにデフォルトの状態へ戻ってしまいます。なので、後で`ライフサイクルフック`を用いてログイン状態を維持する機能を実装します。__
ただその前に、ログインフォームがまだ存在しないので、そちらを先に作成していきます。

<br>

### ログインコンポーネントを実装する

##### 1.`src/components/auth`ディレクトリに、新たに`Login.js`を作成し、まずは、Registration.jsコンポーネントのコードを`そのままコピペ`する。

```react:src/components/auth/Login.js
import React, { useState } from 'react'
import axios from 'axios'

export default function Registration(props) {
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")
    const [passwordConfirmation, setPasswordConfirmation] = useState("")

    const handleSubmit = (event) => {
        axios.post("http://localhost:3001/signup",
            {
                user: {
                    email: email,
                    password: password,
                    password_confirmation: passwordConfirmation
                }
            },
            { withCredentials: true }
        ).then(response => {
            if (response.data.status === 'created') {
                props.handleSuccessfulAuthentication(response.data)
            }
        }).catch(error => {
            console.log("registration error", error)
        })
        event.preventDefault()
    }

    return (
        <div>
            <p>新規登録</p>

            <form onSubmit={handleSubmit}>
                <input
                    type="email"
                    name="email"
                    placeholder="メールアドレス"
                    value={email}
                    onChange={event => setEmail(event.target.value)}
                />
                <input
                    type="password"
                    name="password"
                    placeholder="パスワード"
                    value={password}
                    onChange={event => setPassword(event.target.value)}
                />
                <input
                    type="password"
                    name="password_confirmation"
                    placeholder="確認用パスワード"
                    value={passwordConfirmation}
                    onChange={event => setPasswordConfirmation(event.target.value)}
                />

                <button type="submit">登録</button>
            </form>
        </div>
    )
}
```

##### 2.Registration→`Login関数コンポーネント`になるよう書き換え、`password_confirmationフィールド`は使わないので削除し、axios通信する先のURLを`/login`に変更する。

```react:src/components/auth/Login.js
import React, { useState } from 'react'
import axios from 'axios'

// Login関数コンポーネントへ書き換え
export default function Login(props) {
    // password_confirmationフィールドを削除
    const [email, setEmail] = useState("")
    const [password, setPassword] = useState("")

    const handleSubmit = (event) => {
        // 通信先のURLを/loginに書き換え
        axios.post("http://localhost:3001/login",
            {
                // ここのpassword_confirmationフィールドも削除
                user: {
                    email: email,
                    password: password,
                }
            },
            { withCredentials: true }
        ).then(response => {
            if (response.data.status === 'created') {
                props.handleSuccessfulAuthentication(response.data)
            }
        }).catch(error => {
            console.log("registration error", error)
        })
        event.preventDefault()
    }

    return (
        <div>
            {/* ログインに変更 */}
            <p>ログイン</p>

            {/* フォーム内のpassword_confrmation入力フィールド削除 */}
            <form onSubmit={handleSubmit}>
                <input
                    type="email"
                    name="email"
                    placeholder="メールアドレス"
                    value={email}
                    onChange={event => setEmail(event.target.value)}
                />
                <input
                    type="password"
                    name="password"
                    placeholder="パスワード"
                    value={password}
                    onChange={event => setPassword(event.target.value)}
                />

                <button type="submit">ログイン</button>
            </form>
        </div>
    )
}
```

##### 3.次に、`/login`へPOSTリクエストを送信した際に、正しく返り値を取得できているかどうかを検証する。

```react:src/components/auth/Login.js

...

const handleSubmit = (event) => {
        axios.post("http://localhost:3001/login",
            {
                user: {
                    email: email,
                    password: password,
                }
            },
            { withCredentials: true }
        ).then(response => {
            // 追加
            console.log("login response: ", response)
            // if (response.data.status === 'created') {
            //    props.handleSuccessfulAuthentication(response.data)
            // }
        }).catch(error => {
            console.log("registration error", error)
        })
        event.preventDefault()
    }

...

```

　　・axios通信のthen()内の記述をconsole.log()処理に一旦変更します。

##### 4.`Home.js`に`Login`コンポーネントを表示させる。

```react:src/components/Home.js
...
// 追加
import Login from './auth/Login'

export default function Home(props) {

    const handleSuccessfulAuthentication = (data) => {
        props.handleLogin(data)
        props.history.push("/dashboard")
    }

    return (
        <div>

            ...
            {/* 追加 */}
            <Login />
        </div>
    )
}
```

##### 5.ブラウザで動作確認してみる。

　　・ユーザーデータを入力し、ログインボタンをクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/81016173-bda3-7822-c967-ae7481c445a0.png)

　　・コンソールで返り値を見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/1ccd7338-907e-7971-359f-e0fc43535051.png)

　　・入力したユーザーのデータがRails側で正常にログイン実行がされた結果、正しいユーザーデータが返ってきているので、ログインコンポーネントの方も正常に動作していることがわかります。

　　・それと、`dataフィールドに{ logged_in: true }というデータ`があることに注目してください。これは、`/login`へアクセスした場合にのみ返ってくるデータです。これはこの後axios通信の`then()`の処理にて使用します。

##### 6.`Login`コンポーネントで定義されている`handleSubmit`イベントハンドラの`axios`通信における`then()の条件式`を変更する。

```react:src/components/auth/Login.js
const handleSubmit = (event) => {
        axios.post("http://localhost:3001/login",
            {
                user: {
                    email: email,
                    password: password,
                }
            },
            { withCredentials: true }
        ).then(response => {

            // 変更
            if (response.data.logged_in) {
                props.handleSuccessfulAuthentication(response.data)
            }

        }).catch(error => {
            console.log("registration error", error)
        })
        event.preventDefault()
    }
```

　　・先ほども見たように、ログインした場合にはRailsから`logged_inフィールド`が返ってくるので、これを条件式に設定し、`trueの場合`にログイン処理が実行されるようにします。新規登録の際に行われるログイン処理と差別化するためです。

##### 7.Home.jsコンポーネントでレンダリングしている`Loginコンポーネント`に、`handleSuccessfulAuthentication`イベントハンドラを渡す。

```react:src/components/Home.js
export default function Home(props) {

    const handleSuccessfulAuthentication = (data) => {
        props.handleLogin(data)
        props.history.push("/dashboard")
    }

    return (
        <div>
            <h1>Home</h1>
            <h2>ログイン状態: {props.loggedInStatus}</h2>
            <Registration handleSuccessfulAuthentication={handleSuccessfulAuthentication} />

            {/* 書き加え */}
            <Login handleSuccessfulAuthentication={handleSuccessfulAuthentication} />
        </div>
    )
}
```

<br>

### ログインコンポーネントを実装するまとめ
　1.`src/components/auth`ディレクトリに、新たに`Login.js`を作成し、まずは、Registration.jsコンポーネントのコードを`そのままコピペ`する。
　2.Registration→`Login関数コンポーネント`になるよう書き換え、`password_confirmationフィールド`は使わないので削除し、axios通信する先のURLを`/login`に変更する。
　3.次に、`/login`へPOSTリクエストを送信した際に、正しく返り値を取得できているかどうかを検証する。
　4.`Home.js`に`Login`コンポーネントを表示させる。
　5.ブラウザで動作確認してみる。
　6.`Login`コンポーネントで定義されている`handleSubmit`イベントハンドラの`axios`通信における`then()の条件式`を変更する。
　7.Home.jsコンポーネントでレンダリングしている`Loginコンポーネント`に、`handleSuccessfulAuthentication`イベントハンドラを渡す。

<br>

### ログイン状態を維持する機能を実装する。

ここでは、`useEffect()`というライフサイクルフックを使い、ページレンダリング後に毎回、`ユーザーのログイン状態を追跡`するようにします。これによってログイン状態の維持を実現させます。

##### 1.`App.js`コンポーネントに、ユーザーのログイン状態がチェックできる`ライフサイクル`を定義する。

```react:src/App.js
// useEffectフックを追加
import React, { useState, useEffect } from 'react'
// axiosをimport
import axios from 'axios'
...

import Home from './components/Home'
import Dashboard from './components/Dashboard'

export default function App() {
  const [loggedInStatus, setLoggedInStatus] = useState("未ログイン")
  const [user, setUser] = useState({})

  const handleLogin = (data) => {
    setLoggedInStatus("ログインなう")
    setUser(data.user)
  }

  // 追加
  useEffect(() => {
    checkLoginStatus()
  })

  // 追加
  const checkLoginStatus = () => {
    axios.get("http://localhost:3001/logged_in", { withCredentials: true })
      .then(response => {
      console.log("ログイン状況", response)
    }).catch(error => {
      console.log("ログインエラー", error)
    })
  }

  return (
    ...
  )
}
```

　　・`checkLoginStatus関数`：ここでは、Railsの`"/logged_in"`へ、`withCredentials: true`というデータを含め、GETリクエストを送信しています。

　　・`useEffect()`フックは、`ページがリロードされるたびに毎回`呼び出されます。useEffect()に`checkLoginStatus()関数`を渡すことで、リロード後毎回この関数を呼び出すようにします。

　　・例えば、`/logged_in`へGETリクエストを送信すると、以下で示している`Sessionsコントローラのlogged_in?アクション`へデータが送信されます。ここでは`@current_userの存在`をチェックし、`true`なら`{logged_in: true, user: @current_user}`というJSONオブジェクト、`false`なら`{logged_in: false}`を返します。
![スクリーンショット 2020-08-27 16.02.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/dc405f56-f015-50b4-7a9c-ac2c9d1ba3ee.png)
　　・Railsからの返り値をconsole.log()で示したのが以下の画像です。つまり、`ページがリロードされるたびに、この返り値が返ってくる`ということになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606750/88523977-2a58-c252-da85-8b04793f55c8.png)


##### 2.`checkLoginStatus`の`then()`内で返り値に対する処理を記述する。

```react:src/App.js
const checkLoginStatus = () => {
    axios.get("http://localhost:3001/logged_in", { withCredentials: true })

      .then(response => {
        if (response.data.logged_in && loggedInStatus === "未ログイン") {
          setLoggedInStatus("ログインなう")
          setUser(response.data.user)
        } else if (!response.data.logged_in && loggedInStatus === "ログインなう") {
          setLoggedInStatus("未ログイン")
          setUser({})
        }
      })

      .catch(error => {
        console.log("ログインエラー", error)
    })
}
```

　　・`if (response.data.logged_in && loggedInStatus === "未ログイン")`の部分：返り値に含まれる`data`フィールドの`logged_in`フィールドが`true`なのに、React側の`loggedInStatus`が`"未ログイン"`となっている場合に、`ログイン状態を"ログインなう"に書き換え`し、`userオブジェクトも返り値から受け取ったユーザーオブジェクトに書き換えます`。

　　・`else if (!response.data.logged_in && loggedInStatus === "ログインなう")`の部分：`logged_in`フィールドが`false`なのに、`loggedInStatusが"ログインなう"`になってしまっている場合に、`loggedInStatusオブジェクトを空`にし、`userオブジェクトも空`にします。

<br>

### ログイン状態を維持する機能を実装するまとめ
　1.`App.js`コンポーネントに、ユーザーのログイン状態がチェックできる`ライフサイクル`を定義する。
　2.`checkLoginStatus`の`then()`内で返り値に対する処理を記述する。

<br>

### ログアウト機能を実装する

##### 1.`App.js`コンポーネントに`handleLogout`イベントハンドラを定義する。

```react:src/App.js
...

export default function App() {
  const [loggedInStatus, setLoggedInStatus] = useState("未ログイン")
  const [user, setUser] = useState({})

  ...

  // 追加
  const handleLogout = () => {
    setLoggedInStatus("未ログイン")
    setUser({})
  }

  ...

  return (
    
         ...

              <Home
                {...props}
                handleLogin={handleLogin}
                // 追加する
                handleLogout={handleLogout}
                loggedInStatus={loggedInStatus}
              />
            )}
          />

         ...
  )
}
```

　　・`handleLogout`イベントハンドラが呼び出されることによって、`ログイン状態`・`ユーザーオブジェクト`共に空になるようにします。

##### 2.`Home.js`に、`ログアウトボタン`と、`handleClick`イベントハンドラを定義する。

```react:src/components/Home.js
import React from 'react'
import Registration from './auth/Registration'
import Login from './auth/Login'

export default function Home(props) {

    const handleSuccessfulAuthentication = (data) => {
        props.handleLogin(data)
        props.history.push("/dashboard")
    }

    // handleLogoutClickイベントハンドラ
    const handleLogoutClick = () => {
        axios.delete("http://localhost:3001/logout", { withCredentials: true })
            .then(response => {
                props.handleLogout()
            }).catch(error => console.log("ログアウトエラー", error))
    }

    return (
        <div>
            <h1>Home</h1>
            <p>ログイン状態: {props.loggedInStatus}</p>

            {/* ボタン追加 */}
            <button onClick={handleLogoutClick}>ログアウト</button>

            <Registration handleSuccessfulAuthentication={handleSuccessfulAuthentication} />
            <Login handleSuccessfulAuthentication={handleSuccessfulAuthentication} />
        </div>
    )
}
```

　　・`ログアウトボタン`をクリック→`handleLogoutClick`イベントハンドラ発火→`RailsへDELETEリクエスト送信`→Railsから`返り値を取得`→propsから`handleLogout()`イベントハンドラを呼び出し→ログアウト完了という流れで処理されます。


##最後に

実装は以上で完了です！
React初学者なため、色々とルールに反している部分もあると思うので気付き次第改稿していきます！
ご指摘なども遠慮せずして頂けると、嬉しいです。よろしくお願いします🙇‍♂️