Reactを基本からまとめてみた【13】【Firebaseを使ったログイン機能の実装 ①】

##Firebaseとは
- アプリ開発における『バックエンド環境』を提供するサービス
- 2011年にFirebase社がサービスを開始したが、2014年にgoogleが買収
- mBaaS(エムバース)の１つ　<br>⇨ mobile backend as a Serviceの略で、バックエンド環境をクラウド上で処理してくれるサービスのことをBaaSという　

#####※ BaaSにMobileのmがついたもので、mBaaSもBaaSも同じ意味

##Firebaseのメリット
- バックエンド側の開発コストを抑えることができる<br>⇨ バックエンドに必要な機能が基本的に内包されている為、バックエンド側の開発コストを抑えるるアプリ開発ができる

#####※外部のバックエンドのエンジニアを雇えば、月80万円/人がかかるのが要らない
#####※バックエンドを1から学習すると最低3ヶ月は勉強に費やす


##Firebaseのサイトに移動

公式サイト :　[Firebase]
(https://firebase.google.com/?hl=ja)

##プロジェクトの作成

#####① 画面右上のコンソールへ移動のボタンをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/642a2390-0ade-3652-d7ec-d76d1451c2fa.png)

#####②プロジェクトを追加をクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/550c4066-a64c-d378-1de3-0f16cd9e5e70.png)

#####③プロジェクトの名前を付ける

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/e173b62c-f221-3fbc-866e-7d8f1b3cd303.png)

#####④Google アナリティクスを無効にする
このプロジェクトでGoogleアナリティクスを有効にするが『ON』になっているが
利用しないので『OFF』に設定して”プロジェクトを作成”ボタンをクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/e0a1afb7-576d-8750-a42c-2569f6b4ac82.png)

#####⑤プロジェクト作成ボタンを押し、作成できたら続行ボタンを押す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/8b523e26-512f-a071-b8e4-40a23067a05b.png)

#####プロジェクトの準備完了
プロジェクトの概要ページが表示される。これでプロジェクトの作成は完了。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/9af95db0-5b76-2693-e745-2c68c9cba526.png)



#####⑥Firebaseを追加画面のウェブ</>をクリック

ReactからFirebaseのサービスに接続するための認証情報が必要になるのでプロジェクトの概要画面の左から3番目のボタンをクリックしてアプリの登録を行う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/02d50b0d-4114-088f-0399-fdc88363d98f.png)

#####⑦ アプリのニックネームの登録
アプリの登録を行うためニックネームの設定を行う必要があり、任意の名前をつける。設定したら”アプリ登録”ボタンをクリックする。
#####＊『このアプリのFirebase Hosutingも設定します』にチェックを入れない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/b0aa40d9-f27c-394a-4d87-f38a6b8de209.png)

『アプリの登録』ボタンをクリックするとFirebaseに接続する為の情報が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/0c842998-02d1-59db-fad7-a92cdcfccafd.png)

#####⑧ Firebase SDKの追加
表示されている情報はReactで環境変数として利用するため作成したReactプロジェクトフォルダの直下に.env.localファイルを作成し、下記のソースコードを入力する。
※ " "の中身は、firebaseConfigを確認して入力する。

```
src
 ├── .env
```
```
$ touch .env
```
```
//.env
REACT_APP_FIREBASE_API_KEY="",
REACT_APP_FIREBASE_AUTH_DOMAIN="",
REACT_APP_FIREBASE_DATABASE="https://<PROJECT_ID>.firebaseio.com",
REACT_APP_FIREBASE_PROJECT_ID="",
REACT_APP_FIREBASE_STORAGE_BUCKET="",
REACT_APP_FIREBASE_MESSAGE_SENDER_ID="",
REACT_APP_FIREBASE_APP_ID=""


//REACT_APP_FIREBASE_DATABASE="https://<PROJECT_ID>.firebaseio.com",の　<PROJECT_ID>は、
下記のREACT_APP_FIREBASE_PROJECT_ID="",の""を記載すること
```

#####⑨ .gitignoreファイルに.envを追加
.gitignoreファイルに.envを追加することでgithubにアップロードされない。

```
//省略
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
.env
//省略
```

#####⑩ firebase.jsをsrcフォルダの下に作成し、編集する。

```
src
 ├── firebase
       ├── firebase.js
```
```
$ mkdir firebase
$ touch firebase/firebase.js
```

```firebase.js
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
  apiKey: "",
  authDomain: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: "",
  appId: "",
};

//const app = initializeApp(firebaseConfig);

export const firebaseApp = initializeApp(firebaseConfig);
export const auth = getAuth(firebaseApp);
export const firestore = getFirestore(firebaseApp);
```
#####⑪firebaseパッケージをインストールする。
firebase.jsファイルでは、firebaseに接続するために必要なfirebaseと認証に必要なfirebase/authをimportする。firebaseをimportしていますがReactにデフォルトから含まれているわけではないのでfirebaseパッケージのインストールが必要である。
プロジェクトフォルダ直下でnpmコマンドによりfirebaseパッケージをインストールする。

```
$ npm install --save firebase
$ npm i firebase@9.5.0
```

#####⑫コンソール画面に戻るを押す

##Firebaseの認証の設定

#####①Firebaseの認証(Authentication)の設定を行う。

プロジェクトの概要ページから中央にあるAuthentication（ユーザの認証と管理)をクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/875ade5f-f5a2-537e-ff35-8d08e308f384.png)

#####②プロジェクトの概要から認証をクリック
Authenticationのページが表示されるので”始まる”ボタンをクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/d5c83e5e-5f9f-1a47-0670-47a571bf56ab.png)


#####③Authenticationページを編集する
ユーザがサインイン(ログイン)するための方法の一覧が表示される。Googleアカウントなども利用することができるが、今回はメール/パスワードを利用する。

※ 理由：一般的なサービスではユーザ登録としてメールアドレスを登録してもらうことが多いから　
メールアドレスとパスワードを使用して登録を有効にしてください。有効にしたら”保存”ボタンをクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/87453456-a070-9419-6691-54d70780869f.png)

メール/パスワードでの設定
保存後はメール/パスワードのステータスのみ”有効”になっていることが確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/c2558886-d4f1-405c-d6a3-3276e72605cf.png)

#####①『auth』ディレクトリを作成し、こちらに認証機能系は集約する。

```
src
 ├── auth
    ├── AuthProvider.js
    └── Login.js
    └── PrivateRoute.js
    └── SignUp.js
 ├── components
 ├── firebase
 ├── App.js
```
```
$ mkdir src/auth
$ touch src/auth/AuthProvider.js
$ touch src/auth/Login.js
$ touch src/auth/PrivateRoute.js
$ touch src/auth/SignUp.js
```

#####② App.jsにログイン状態で表示ページを変える為、Routerを作成する。
exactはpathが「含む」とならないように指定。

```src/App.js
import React from "react";
import './App.css';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import PrivateRoute from "./auth/PrivateRoute";
import { AuthProvider } from "./auth/AuthProvider";
import Home from "./components/Home";
import Login from "./auth/Login";
import SignUp from "./auth/SignUp";

const App = () => {
  return (
    <AuthProvider>
      <Router>
        <Switch>
          <PrivateRoute exact path="/" component={Home} />
          <Route exact path="/login" component={Login} />
          <Route exact path="/signup" component={SignUp} />
        </Switch>
      </Router>
    </AuthProvider>
  );
};
export default App;
```

#####③ AuthPrivider.jsを編集する。
認証の情報（ユーザーがログイン、サインアップする）は、このファイルで作成する。ユーザー情報が必要なコンポーネントでuseContextを使用する。
* 通常データはトップダウン形式でpropsを渡さないといけないですが、contextを使うことで、コンポーネントツリーに簡単にデータを共有することができる。


```src/auth/AuthProvider.js
import React, { useEffect, useState } from "react";
import { auth } from "../firebase/Firebase";

const AuthContext = React.createContext() 

const AuthProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null);

  //サインアップ後認証情報を更新
  const signup = async (email, password, history) => {
    try { 
      await auth.createUserWithEmailAndPassword(email, password);
      auth.onAuthStateChanged(user => setCurrentUser(user));
      history.push("/");
    } catch (error) {
      alert(error);
    }
  };

  //ログインさせる
  const login = async (email, password, history) => {
    try {
      await auth.signInWithEmailAndPassword(email,password);
      auth.onAuthStateChanged(user => setCurrentUser(user));
      history.push("/");
    } catch (error) {
      alert(error);
    }
  }

  //初回アクセス時に認証済みかチェック
  useEffect(() => {
    auth.onAuthStateChanged(setCurrentUser);
  }, []);

  return (
    <AuthContext.Provider value={{ signup, login, currentUser}}>
      {children}
    </AuthContext.Provider>
  )
}
export {AuthContext, AuthProvider}
```

初期値のユーザーのステートはnull。

#####signup関数
引数にemail、password、historyを渡して非同期処理を行う。
auth.createUserWithEmailAndPassword(email, password)は、
firebaseのメソッドでemailとpasswordを元にアカウントが作成される。
その後userの情報を取得し、CurrentUserにセットする。

######history.push（"/")は、reactRouterの画面遷移させる機能で、今回はログイン後、Home画面に行くようにする。

#####login関数
同じ様に、ユーザーがログインしたら情報を取得しCurrentUserを更新するようにする。
auth.signInWithEmailAndPassword(email,password)
これもまたfirebaseのメソッドである。

あとは、最初にログインしてるか確認する為、useEffectで一回だけauth.onAuthStateChangedを実行する。
※一回だけ実行したいので、第二引数には空の配列[]を渡します。

#####④ PrivateRoute.jsを編集する。
アプリのメイン画面Home.jsは、PrivateRouteに指定する。
ここで、ユーザーが『ログインしてれば => メイン画面を表示』
『未ログインの場合 => ログイン画面を表示』の作業を行なう。

```src/auth/PraveteRoute.js
import React, { useContext } from "react";
import { Route } from "react-router-dom";
import { AuthContext } from "./AuthProvider";
import Login from "./Login";

const PrivateRoute = ({ component, ...rest }) => {
  const { currentUser } = useContext(AuthContext);
  //AuthContextからcurrentUserを受け取る

  const renderingComponent = currentUser ? component : Login;
  //currentUserがtrueの場合component＝Home、falseならLoginコンポーネントにroute

  return <Route {...rest} component={renderingComponent} />;
};

export default PrivateRoute;
```

...restは、残りのpropsをまとめて引数に渡す（今回、他のpropsはない）

#####⑤SignUp.jsとLogin.jsを編集する。
SignUp.jsコンポーネントでは、ユーザー登録画面の表示、登録内容を取得する。
handleSubmitが実行される時、入力されたemailとpasswordの内容をAuthProviderで作ったsignup関数の引数に渡してデータが登録される。
アップデート後のhistory（情報）を渡すために、最後exportの時withRouter(SignUp)を使う。

```src/auth/SignUp.js
import React, { useContext } from "react";
import { withRouter } from "react-router";
import { Link } from 'react-router-dom'
import { AuthContext } from "./AuthProvider";
import { styled } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';

const SignUpButton = styled(Button)({
  background: '#f16272',
  fontSize: '1.8rem',
  border: 0,
  borderRadius: 3,
  color: 'white',
  padding: '10px 40px',
  marginTop: '30px',
  '&:hover': {
    backgroundColor: '#ee3e52',
  },
});

const SignUp = ({ history }) => {
  const { signup } = useContext(AuthContext);
  //AuthContextからsignup関数を受け取る

  const handleSubmit = event => {
    event.preventDefault();
    const { email, password } = event.target.elements;
    signup(email.value, password.value, history);
  };

  return (
    <div className="wrapper">
      <div className="auth-container">
        <h1>Sign Up</h1>
        <form className="auth-form" onSubmit={handleSubmit}>
          <div className="auth-form-item">
            <label>E-mail Address</label>
            <input name="email" type="email" placeholder="email@gmail.com" />
          </div>
          <div className="auth-form-item">
            <label>Password</label>
            <input name="password" type="password" placeholder="Password"/>
          </div>
            <SignUpButton className="signUp-btn" type="submit">SIGN UP</SignUpButton>
        </form>
        <Link to="/login" className="auth-bottom" >SignInへ戻る</Link>
      </div>
    </div>
  );
};
export default withRouter(SignUp);
```

Login.jsも同様に、signup部分をloginに変えて編集する。

```src/auth/Login.js
import React, { useContext } from "react";
import { withRouter } from "react-router";
import { Link } from 'react-router-dom'
import { AuthContext } from "./AuthProvider";
import "firebase/auth";
import { styled } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';

const SignInButton = styled(Button)({
  background: '#6fc4f9',
  fontSize: '1.8rem',
  border: 0,
  borderRadius: 3,
  color: 'white',
  padding: '10px 40px',
  marginTop: '30px',
  '&:hover': {
    backgroundColor: '#57baf8',
  },
});


const Login = ({ history }) => {
  const { login } = useContext(AuthContext);
  //AuthContextからlogin関数を受け取る

  const handleSubmit = event => {
    event.preventDefault();
    const { email, password } = event.target.elements;
    login(email.value, password.value, history);
  };

  return (
    <div className="wrapper">
      <div className="auth-container">
        <h1>Sign In</h1>
        <form className="auth-form" onSubmit={handleSubmit}>
          <div className="auth-form-item">
            <label>E-mail Address</label>
            <input name="email" type="email" placeholder="email@gmail.com" />
          </div>
          <div className="auth-form-item">
            <label>Password</label>
            <input name="password" type="password" placeholder="Password"/>
          </div>
            <SignInButton type="submit">SIGN IN</SignInButton>
        </form>
        <Link to="/signup" className="auth-bottom" >SignUpはこちら</Link>
      </div>
    </div>
  );
};

export default withRouter(Login);
```

##Cloud Firestore の設定

###Firestoreとは
Google社が提供するNoSQL型のデータベースのこと。
特徴として、リアルタイムのデータを受信できる、つまりDB側のデータが変更されるとすぐに、クライアント側に反映される仕組みを提供している。

###Firestore内でのデータ構造のイメージ

####collection

- 複数のdocumentを保管する所
- 複数のtaskをまとめて保管している封筒

####document

- 実際にdataが記述されている所 
- ここが１つ１つのtaskの詳細について書いてある用紙

####data

- taskの詳細 
- title,completed
- 用紙に実際に記述してある事柄


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/0f065a6c-1aab-600d-e372-d8d9aaac569a.png)



#####①Cloud Firestoreを選択し、『データベースの作成』する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/e8074a05-71d8-6456-cf6b-26be2563f5c2.png)

#####②今回は、学習が目的なので『テストモード』を選択し、『次へ』『有効にする』を押す。
※ 誰でもデータの取得や更新ができるモードで、この設定は後で変更できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/de5b943f-5ca6-b128-dbf1-15e404fe1cfb.png)

#####③『コレクションを開始』を選択し、コレクションIDを入力する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/777e8690-baad-629a-2caf-a6857b0f80c9.png)

#####④ドキュメントIDは自動を選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/fcd77c6b-c820-b9fa-8a32-ad1ed1858300.png)

#####⑤フィールドを定義する。

#####例

フィールド 　  タイプ　　  値
『title』　  『string』  『taskA』
『completed』『booleas』 『false』
『dataTime』 『timestamp』  


##参考サイト
[【Firebase入門】#1 ~Firebaseの理解~]
(https://www.youtube.com/watch?v=FsFEEFP3yTk&t=6s)
[【Firebase入門】#2 ~Firebaseの初期設定~]
(https://www.youtube.com/watch?v=XOvJd95JTgE)
[【Firebase入門】#3-1 ~Firestoreとの連携前編~：全件取得機能まで]
(https://www.youtube.com/watch?v=aAwcuLoDqO4&t=1260s)
[[Firebase] Firestoreで読み書きする (Web編)]
(https://blog.katsubemakito.net/firebase/firestore_readwrite_1)