概要
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 (デフォルト) |