Firebase Authenticationでログイン認証・メールアドレス変更までやってみた

Firebaseを使用したReactのWEBアプリで、
Authenticationを使ってログイン認証を実装しました。

## ログイン認証

まずAuthProvider.jsを作成。

```React:AuthProvider.js
import React, { useEffect, useState } from 'react';
import { auth, db } from '../firebase';

export const AuthContext = React.createContext();

export const AuthProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [emailText, setEmailText] = useState('');

  const login = async (email, password, history) => {
    try {
      await auth.signInWithEmailAndPassword(email, password);
      history.push('/');
    } catch (error) {
      alert(error);
    }
  };

  const signup = async (email, password, name, history) => {
    try {
      const { user } = await auth.createUserWithEmailAndPassword(
        email,
        password
      );
      const docId = Math.random().toString(32).substring(2);
      db.collection('displayName').doc(docId).set({
        uid: user.uid,
        name,
      });
      history.push('/');
    } catch (error) {
      alert(error);
    }
  };

  function updateEmail(email) {
    debugger;
    return currentUser.updateEmail(email);
  }

  const value = {
    currentUser,
    loading,
    login: login,
    signup: signup,
    updateEmail,
    emailText,
    setEmailText,
  };

  useEffect(() => {
    const unsubscribed = auth.onAuthStateChanged((user) => {
      setCurrentUser(user);
      setLoading(false);
    });
    return () => {
      unsubscribed();
    };
  }, [currentUser]);

  auth.onAuthStateChanged(setCurrentUser);

  return (
    <AuthContext.Provider value={value}>
      {!loading && children}
    </AuthContext.Provider>
  );
};

```

`history.push('/');`については、App.jsで
react-router-domを使用したReactでのルーティング設定を行なっています。

```React:PrivateRoute.js
const PrivateRoute = ({ component: RouteComponent, ...options }) => {
  const { currentUser } = useContext(AuthContext);
  const Component = currentUser ? RouteComponent : Login;

  return <Route {...options} component={Component} />;
};

export default PrivateRoute;
```


```React:App.js
<AuthProvider>
      <Router>
        <div>
          <PrivateRoute exact path="/" component={Home} />
          <Route exact path="/Signup" component={Signup} />
          <Route exact path="/Login" component={Login} />
        </div>
      </Router>
 </AuthProvider>
```

こちらの設定をしているので、
ログインがされていると認証された場合は、Home.jsが表示されるようになっています。

ログイン認証がされてHome.jsが表示されるまでに、一瞬Login.jsが表示されてしまったので
そこはAuthProvider.jsで

```React:AuthProvider.js
  useEffect(() => {
    const unsubscribed = auth.onAuthStateChanged((user) => {
      setCurrentUser(user);
      setLoading(false);
    });
    return () => {
      unsubscribed();
    };
  }, [currentUser]);
```
この設定をすることで、認証されるまでローディングさせて
認証ができたらホーム画面が表示されるようにしています。




## ログイン後の画面で、ログイン情報のメールアドレスを変更
続いて、ログイン後の画面。
（長いので該当箇所以外は割愛します）

```React:Home.js
const {
    currentUser,
    updateEmail,
    setEmailText,
    emailText,
  } = useContext(AuthContext);

const emailHandleChange = (e) => {
    setEmailText(() => e.target.value);
  };

function changeEmail() {
    if (emailText === '') {
      //条件に一致する場合(メールアドレスが空の場合)
      alert('メールアドレスを入力してください'); //エラーメッセージを出力
      return false; //送信ボタン本来の動作をキャンセルします
    } else {
      //条件に一致しない場合(メールアドレスが入力されている場合)
      updateEmail(emailText)
        .then(() => {
          alert('メールアドレスを更新しました');
        })
        .catch((error) => {
          console.log(error);
          debugger;
          alert('メールアドレス更新に失敗しました');
        });
    }
}

return (
    <div>
       <div>
        <h2>メールアドレスを変更</h2>
        <p>現在登録のメールアドレス</p>
        <p>{currentUser.email}</p>
        <p>変更後のメールアドレス</p>
        <form
          noValidate
          autoComplete='off'
          name='email_form'
        >
          <TextField
            id='outlined-basic'
            variant='outlined'
            name='email'
            type='text'
            onChange={emailHandleChange}
          />
          <Button
            className={classes.submit}
            type='button'
            onClick={changeEmail}
          >
            メールアドレスを変更
          </Button>
        </form>
      </div>

　　　　　　　  　<div className='btn_field'>
        <Button className={classes.submit} onClick={() => auth.signOut()}>
          Sign out
        </Button>
      </div>
    </div>
  );
```
ButtonとかTextFieldはMaterial-UIのコンポーネントを使っています。
ログイン後、Home.jsには現在ログイン中のメールアドレスを`{currentUser.email}`で表示させ
変更したい場合はその下のテキストフィールドに値を入れ
「メールアドレスを変更」ボタンをクリックしたら、`changeEmail`が走って
メールアドレスが変更される流れになります。

メールアドレスを変更するボタンのtypeを最初submitにしていたのですが
そうするとボタンを押したあと、ホーム画面ではなく、ログイン画面に勝手にリロードされてしまいました。
参考：[How do I make an HTML button not reload the page](https://stackoverflow.com/questions/1878264/how-do-i-make-an-html-button-not-reload-the-page)

typeをbuttonに変更したら、メールアドレスを更新後もログイン後の状態で留まってくれて
メールアドレスの変更もきちんとされていました。
これがなぜなのかはいまだに理解できていません・・・


## まとめ
React超初心者なので、現役エンジニアの方にアドバイスいただきながら作成しました。
特にメールアドレスを変更する部分は、調べても全然参考になりそうな記事とかが見当たらなかったので苦戦しました。



この流れで、ログイン後にパスワード編集もできるようにやっていきたいと思います◎


## 参考サイト
[Firebase公式 Firebase でユーザーを管理する](https://firebase.google.com/docs/auth/web/manage-users#web-v9_5)