【Firebase】Firebase Authentication メール＆Google認証の実装（前編）【React】

## 目指すところ
こんにちは
FirebaseAuthenticationをReactプロジェクトに実装を試みました
**◆目指すところ**
2つ認証方法でサインアップ（メール、Google）
**◆本記事のオチ**
メール認証…OK、Google認証…YET

タイトルの失敗談とは、取り組んでる間にエラーに見舞われ本目的（メールとGoogle認証）に夢半ばに挫けた結果となった為です
※FirebaseAuthenticationについては割愛
こういった経緯を綴ります
## 環境
Windows10
Firebase 9.15
React 18.2
Node.js 18.12.1
## 作業内容
### 基本参考にするサイト
まずはじめにこちらの2つのサイトを基本として手順をすすめた
このサイトの手順に従ってコードを書く
記事ごとコードが散らばってますが差分に注目していただければ幸いです

https://ralacode.com/blog/post/react-firebase-authentication/

https://reffect.co.jp/react/react-firebase-auth#Google

これから、コケたところをかいつまんで書きます
### ディレクトリ構成
構成はRalacodeさんに準拠
[src]
├─[components]
｜ ├ Home.js
｜ ├ Login.js
｜ ├ Register.js
｜ ├ PrivateRoute.js
｜ └ PublicRoute.js
├─[context]
｜ └ AuthContext.js
├─App.js
├─App.css
├─firebase.js
└─Page404.js
### まず、メール認証
元のコード
```Login.js
import { useState } from 'react';
import { createUserWithEmailAndPassword } from "firebase/auth";
const SignUp = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const handleSubmit = (event) => {
    event.preventDefault();
const { email, password } = event.target.elements;
    auth.createUserWithEmailAndPassword(email.value, password.value);  };
```
上のサンプルコードをそのまま適用し、適当なメールアドレス（xxx@xxx.xxx）でFirebaseにサインアップを試みたが下記のエラーが返ってきた
> Uncaught (in promise) TypeError: can't assign to property "_canInitEmulator" on "xxx@xxx.xxx"

https://zenn.dev/knaka0209/articles/9472e94612a6f0

こちらのサイトを参考にする
auth.createUserWithEmailAndPassword と書くとエラーが吐かれたと推測し、
createUserWithEmailAndPassword(auth, ...) に修正
import { auth } from '../firebase'; も追加すると正常に動作
修正コード

```Login.js
import { useState } from 'react';
import { auth } from '../firebase';
import { createUserWithEmailAndPassword } from "firebase/auth";

const SignUp = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const handleSubmit = (event) => {
    event.preventDefault();
const { email, password } = event.target.elements;
    createUserWithEmailAndPassword(auth, email.value, password.value)
  };
```
### React Routerエラー
そして、参照サイトのコードをコピペで進んでいた私に待ち構えていたのは、ReactRouterのエラーでした
元のコード
```App.js
import SignUp from './components/SignUp';
import { AuthProvider } from './context/AuthContext';
import { BrowserRouter, Route } from 'react-router-dom';

function App() {
  return (
    <AuthProvider>
      <div style={{ margin: '2em' }}>
        <BrowserRouter>
          <Route path="/signup" component={SignUp} />
        </BrowserRouter>
      </div>
    </AuthProvider>
  );
}

export default App;
```
このあと出たエラーコード
> \<Route\> is only ever to be used as the child of \<Routes\> element, never rendered directly. Please wrap your Route in a Routes.

包めといわれても…
エラーコンソールをよく見ると
> Please wrap your Route in a Routes.

からのー…あり？
エラーコードをググって以下のサイトにたどり着き修正していく

https://stackoverflow.com/questions/69832748/error-error-a-route-is-only-ever-to-be-used-as-the-child-of-routes-element

(変更前)
```App.js
<BrowserRouter>
<Route path="/signup" conponent={SignUp} />
</BrowserRouter>
```
(変更後)
```App.js
import { BrowserRouter, Route, Routes } from 'react-router-dom';
…略…
<BrowserRouter>
<Routes>
<Route path="/signup" element={<SignUp />} />
</Routes>
</BrowserRouter>
```
なるほど、RouteをRoutesで囲めということを理解
importにRoutesを追加
Routeの親にRoutesを追加（=Routesの子にRoute）
Routeの中、conponentのままではまだエラーが返ってきたので
elementを試してみる（componentからelementに変更）と正常に動作した

再度メールでFirebaseにサインインを試すとメール認証経由で、Firebase Authenticationにユーザー新規登録されていることも確認した（よし）
### 次に、アクセス制限設定
メール認証がクリア出来たのでアクセス制限設定に取り掛かる
また次のサイトを参考にしてとりあえずコピペで進めた

https://reffect.co.jp/react/react-firebase-auth#Google
### 使われてないコマンドでコケる
その名の通り、Reactバージョンアップに伴いコマンド名が大々的に変わったようです
元のコード
```Home.js
import { auth } from '../firebase';
import { useHistory } from 'react-router-dom';

const Home = () => {
  const history = useHistory();
  const handleLogout = () => {
    auth.signOut();
    history.push('/login');
  };
  return (
    <div>
      <h1>ホームページ</h1>
      <button onClick={handleLogout}>ログアウト</button>
    </div>
  );
};

export default Home;
```
そして、コンソールに次のエラーが返ってきた
> export 'useHistory' (imported as 'useHistory') was not found in 'react-router-dom'

https://stackoverflow.com/questions/62861269/attempted-import-error-usehistory-is-not-exported-from-react-router-dom

> In react-router-dom v6 useHistory() is replaced by useNavigate().

これか。
修正コード
```Home.js
import { auth } from '../firebase';
import { useNavigate } from 'react-router-dom';

const Home = () => {
  const navigate = useNavigate();
  const handleLogout = () => {
    auth.signOut();
    navigate('/login');
  };
  return (
    <div>
      <h1>ホームページ</h1>
      <button onClick={handleLogout}>ログアウト</button>
    </div>
  );
};

export default Home;
```
useHistoryをuseNavigateに置き換え、関係するところも修正、pushは消した
（Reactのバージョンによってコマンド表記・役割が変わることが判明。厄介だな。今後注意しよう）
この記事も参考にした

https://qiita.com/tomoronn3/items/2496c29878e9f8ae798e

書き換えるとエラーは表示されなくなった

次に進む
元のコード＋上記の修正分
```Home.js
import { auth } from '../firebase';
import { useNavigate, Redirect } from 'react-router-dom';
import { useAuthContext } from '../context/AuthContext';
const Home = () => {
  const navigate = useNavigate();
  const { user } = useAuthContext();
  const handleLogout = () => {
    auth.signOut();
    navigate('/login');
  };

  if (!user) {
    return <Redirect to="/login" />;
  } else {
    return (
      <div>
        <h1>ホームページ</h1>
        <button onClick={handleLogout}>ログアウト</button>
      </div>
    );
  }
};

export default Home;
```
はい、出ましたエラーはこちら

> export 'Redirect' (imported as 'Redirect') was not found in 'react-router-dom'

https://stackoverflow.com/questions/63690695/react-redirect-is-not-exported-from-react-router-dom

またこれか（Reactのバージョンごとのコマンド違い問題）
RedirectはNavigateに統合されたことを知り修正
修正コード
```Home.js
import { auth } from '../firebase';
import { useNavigate, Navigate } from 'react-router-dom';
import { useAuthContext } from '../context/AuthContext';
const Home = () => {
  const navigate = useNavigate();
  const { user } = useAuthContext();
  const handleLogout = () => {
    auth.signOut();
    navigate('/login');
  };

  if (!user) {
    return <Navigate to="/login" />;
  } else {
    return (
      <div>
        <h1>ホームページ</h1>
        <button onClick={handleLogout}>ログアウト</button>
      </div>
    );
  }
};

export default Home;
```
### PrivateRouteを設定
手順の通りコピペでファイルを作った
```PrivateRoute.js
import { Route, Redirect } from 'react-router-dom';
import { useAuthContext } from '../context/AuthContext';
const PrivateRoute = ({ component: Component, ...rest }) => {
  const { user } = useAuthContext();
  return (
    <Route
      {...rest}
      render={(routeProps) => {
        return user ? <Component {...routeProps} /> : <Redirect to="/login" />;
      }}
    />
  );
};

export default PrivateRoute;
```
上記で出てきた話。RedirectをNavigateに書き換え（修正コードは省略）
App.jsファイルでRouteコンポーネントを作成したPrivateRouteに変更します。
```App.js
import Home from './components/Home';
import SignUp from './components/SignUp';
import Login from './components/Login';
import { AuthProvider } from './context/AuthContext';
import { BrowserRouter, Route } from 'react-router-dom';
import PrivateRoute from './components/PrivateRoute';

function App() {
  return (
    <AuthProvider>
      <div style={{ margin: '2em' }}>
        <BrowserRouter>
          <PrivateRoute exact path="/" component={Home} />
          <Route path="/signup" component={SignUp} />
          <Route path="/login" component={Login} />
        </BrowserRouter>
      </div>
    </AuthProvider>
  );
}

export default App;
```
そうしてエラーコンソールには…
> Uncaught Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>

エラーメッセージでいくつかググったがまだこのエラーは解決していない

## ここで脱線します
ReactRouterエラーで試行錯誤していた矢先、突然、プロジェクトのゴキゲンが悪くなった。どうやらタイミング的にこの2つのコマンドを同時に実行してからだ（それが原因かは不明）
```
>npm start
>npm audit fix --force
```
localhostにサーブしてみて得られたエラーコードでぐぐって試してみたがなんとも直らず…
（エラーコードと参考サイトは省略）
なんだかんだでたどり着いたnode moduleがあやしい…これで直ってくれと天に祈る

https://zenn.dev/mo_ri_regen/articles/node-modules-article

直らなかった…



```
>npm start
```
確認にもう一度localhostにサーブしてみる。
→やはりエラー
こちらがエラー画面（画像）

```
>npm audit --force
```
エラーコードではなくエラーの中身に注目
パッケージが壊れてるっぽいので一旦削除してリビルドしてみる。
```
>npm r "エラーで表示された開発が終了されていたパッケージとか"
```
そうしてリビルドすること…
npm start を試してみる

```
>npm start
```
無事にlocalhostにサーブできました。
戻ってきた！

```
>npm run build
```

こちらも問題なく動いたので一安心です

さてここで大問題…
**FirebaseAuthentication、Googleでのログイン実装がまだできてない**
悲しい…
とりあえず本記事はここで筆を置きます
ここまで読んでいただきありがとうございました

後編に続く…！？
続きます…！！！！
### FirebaseAuthentication公式ドキュメントの紹介

https://firebase.google.com/docs/auth/web/google-signin

やっぱり公式ドキュメント最強説、ということで少し読んだ

## 反省＋今後の課題
* FirebaseAuthentication、Google認証の実装はできなかった
* Node.jsの仕組みを理解する
* Docker環境でReact+Firebase構築を学ぶ（将来用に）
* FirebaseAuthentication、Google認証の実装する（次回投稿予定）