概要
Firebase AuthenticationとRecoilを使った、ユーザー認証部分の処理を紹介します。
状態管理ライブラリにRecoilを使うことで、redux tool kitやuseContextを使うよりもシンプルに実装することができます。
環境
- windows10
- VSCode
- npm
アーキテクチャ
コンポーネントは、3つあります。
- App.tsx:起点となるコンポーネント
- Auth.tsx:認証を行うコンポーネント
- Main.tsx:ログインしたユーザーが閲覧できるコンポーネント
処理の流れ
① アプリケーションを起動したときにまずApp.tsxが呼ばれます。ログアウト状態から始まるので、Auth.tsxへ遷移します。
② Firebase Authに登録しているユーザーを使って、ログインします。
③ Firebase Authのログイン状態をオブザーバーで監視します。
④ ③により、ログイン状態が変更されたときに、その状態をRecoilに保存します。
⑤ Recoilの状態が変更されたことによって、コンポーネントが再レンダリングされます。
⑥ App.tsxで再びログイン状態が評価されて、Main.tsxへ遷移します。
また、Main.tsxでログアウトした場合、オブザーバー処理によって④が実行されるので、Auth.tsxに遷移します。
Recoil
Recoilとはなんぞや?
Recoilの使い方
公式ドキュメント
プロジェクトの作成
Firebase
以下を参考に、プロジェクトを作成してください。
また、Authenticationの初期設定も行います。このとき、Mail & Passwordを選択します。(お好きな認証方法で大丈夫です)

CRA(Create React Application)
Reactアプリケーションは、CRA(Create React Application)を使って作成します。
プロジェクトフォルダを作成して、そこで以下を実行します。
npx create-react-app . --template typescript
プロジェクトに必要なパッケージをインストールします。
npm i firebase recoil
npm i -D @types/recoil
コーディング
Firebase インスタンスの作成
root直下に.env.localを追加します。ここに、Firebaseプロジェクトの環境変数を記入します。
CRAでは、環境変数名をREACT_APP_で始めることで、環境変数として自動的に認識されるようになります。
REACT_APP_FIREBASE_API_KEY="apiKey"
REACT_APP_FIREBASE_AUTH_DOMAIN="authDomain"
REACT_APP_FIREBASE_DATABASE_URL="https://<projectId>.firebaseio.com"
REACT_APP_FIREBASE_PROJECT_ID="projectId"
REACT_APP_FIREBASE_STORAGE_BUCKET="storageBucket"
REACT_APP_FIREBASE_MESSAGING_SENDER_ID="messagingSenderId"
REACT_APP_FIREBASE_APP_ID="appId"
src配下にfirebase.tsを追加します。
import 'firebase/auth';
import firebase from 'firebase/app';
const firebaseConfig = {
	apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
	authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
	databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
	projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
	storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
	messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
	appId: process.env.REACT_APP_FIREBASE_APP_ID
};
if (!firebase.apps.length) firebase.initializeApp(firebaseConfig);
export const auth = firebase.auth();
Recoil State
ログイン状態を保存しておくための処理を追加します。
import { atom } from 'recoil';
export const signInUserState = atom({
	key: 'auth/signIn',
	default: {
		uid: ''
	}
});
保持する状態は、ログインユーザーのuidです。
Firebase Auth 処理
Firebase Authに関する処理を追加します。
import { useEffect } from 'react';
import { useRecoilState, useResetRecoilState } from 'recoil';
import { signInUserState } from '../store/auth';
import { auth } from './firebase';
/**
 * ユーザー認証する
 */
export const signIn = async (email: string, password: string) => {
	try {
		await auth.signInWithEmailAndPassword(email, password);
	} catch (error) {
		alert('サインイン認証に失敗しました。');
	}
};
/**
 * ユーザー登録する
 */
export const signUp = async (email: string, password: string) => {
	try {
		await auth.createUserWithEmailAndPassword(email, password);
	} catch (error) {
		alert('ユーザー登録に失敗しました。');
	}
};
/**
 * サインアウトする
 */
export const signOut = async () => {
	try {
		await auth.signOut();
	} catch (error) {
		alert('サインアウトに失敗しました。');
	}
};
/**
 * SignInの状態を監視する
 */
export const useAuth = () => {
	const [signInUser, setSignInUser] = useRecoilState(signInUserState);
	const resetStatus = useResetRecoilState(signInUserState);
	useEffect(() => {
		const unSub = auth.onAuthStateChanged(authUser => {
			if (authUser) {
				setSignInUser({
					uid: authUser.uid
				});
			} else {
				resetStatus();
			}
		});
		return () => unSub();
	}, [setSignInUser, resetStatus]);
	return signInUser;
};
useAuthは、カスタムフックになっています。
onAuthStateChangedでユーザーのログイン状態を監視し、ログイン状態なら(authUserが取得できたら)、setSignInUserを使ってRecoil Stateにuidを保存します。ログアウト状態なら、resetStatusを使ってRecoil Stateの状態を初期化します。
公式ドキュメント
コンポーネント
Recoilを扱うためには、コンポーネントをRecoilRootで囲う必要あります。
import './index.css';
import React from 'react';
import ReactDOM from 'react-dom';
import { RecoilRoot } from 'recoil';
import { App } from './components/App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
	<React.StrictMode>
		<RecoilRoot>
			<App />
		</RecoilRoot>
	</React.StrictMode>,
	document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Recoilは、コンポーネント全体をRecoilRootで囲っても、Stateの変化に関係するコンポーネントしか再レンダリングされません。
import React from 'react';
import { useAuth } from '../firebase/authFunctions';
import { Auth } from './Auth';
import { Main } from './Main';
export const App: React.FC = () => {
	const signInUser = useAuth();
	return <>{signInUser.uid ? <Main /> : <Auth />}</>;
};
App.tsxでカスタムフックを呼出し、uidの有無で、MainコンポーネントかAuthコンポーネントに遷移させます。
Main.tsxとAuth.tsxは、割愛します。
まとめ
Recoilを使うと、Redux Tool Kitを使うより簡単に実装できました。
おまけ
Authコンポーネントのテンプレート
Material UIでAuth.tsxを作成する場合、テンプレートを使うと簡単に作成できます。
ログインユーザー(authUser)から取得できる値
本記事では、uidのみ取得していますが、他にも色々取得できます。
| プロパティ | 型 | 説明 | 
|---|---|---|
| uid | 文字列 | 新しく作成されたユーザーに割り当てる uid。1~128 文字の文字列を指定します。指定されていない場合は、uid が自動的に生成される | 
| 文字列 | ユーザーのプライマリ メールアドレス | |
| emailVerified | ブール値 | ユーザーのプライマリ メールアドレスが確認されているかどうか。指定されていない場合、デフォルト値は false | 
| phoneNumber | 文字列 | ユーザーのメインの電話番号 | 
| password | 文字列 | ユーザーのハッシュ解除された未加工のパスワード。6 文字以上 | 
| displayName | 文字列 | ユーザーの表示名 | 
| photoURL | 文字列 | ユーザーの写真 URL | 
| disabled | ブール値 | ユーザーが無効かどうか。無効の場合は true、有効の場合は false (デフォルト) | 

