LoginSignup
20
12

More than 1 year has passed since last update.

【React】Firebase × Recoil を使った認証機能の実装方法

Last updated at Posted at 2021-07-26

概要

Firebase AuthenticationRecoilを使った、ユーザー認証部分の処理を紹介します。
状態管理ライブラリにRecoilを使うことで、redux tool kituseContextを使うよりもシンプルに実装することができます。

環境

  • windows10
  • VSCode
  • npm

アーキテクチャ

アーキテクチャ.png

コンポーネントは、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

以下を参考に、プロジェクトを作成してください。

Firebaseプロジェクトの作成

また、Authenticationの初期設定も行います。このとき、Mail & Passwordを選択します。(お好きな認証方法で大丈夫です)
無題.png

CRA(Create React Application)

Reactアプリケーションは、CRA(Create React Application)を使って作成します。
プロジェクトフォルダを作成して、そこで以下を実行します。

cmd
npx create-react-app . --template typescript

プロジェクトに必要なパッケージをインストールします。

cmd
npm i firebase recoil
npm i -D @types/recoil

コーディング

Firebase インスタンスの作成

root直下に.env.localを追加します。ここに、Firebaseプロジェクトの環境変数を記入します。
CRAでは、環境変数名をREACT_APP_で始めることで、環境変数として自動的に認識されるようになります。

.env.local
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を追加します。

src/firebase/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

ログイン状態を保存しておくための処理を追加します。

src/store/auth.ts
import { atom } from 'recoil';

export const signInUserState = atom({
    key: 'auth/signIn',
    default: {
        uid: ''
    }
});

保持する状態は、ログインユーザーのuidです。

Firebase Auth 処理

Firebase Authに関する処理を追加します。

src/firebase/authFunctions.ts
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で囲う必要あります。

src/index.tsx
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の変化に関係するコンポーネントしか再レンダリングされません。

src/components/App.tsx
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.tsxAuth.tsxは、割愛します。

まとめ

Recoilを使うと、Redux Tool Kitを使うより簡単に実装できました。

おまけ

Authコンポーネントのテンプレート

Material UIAuth.tsxを作成する場合、テンプレートを使うと簡単に作成できます。

ログインユーザー(authUser)から取得できる値

本記事では、uidのみ取得していますが、他にも色々取得できます。

プロパティ 説明
uid 文字列 新しく作成されたユーザーに割り当てる uid。1~128 文字の文字列を指定します。指定されていない場合は、uid が自動的に生成される
email 文字列 ユーザーのプライマリ メールアドレス
emailVerified ブール値 ユーザーのプライマリ メールアドレスが確認されているかどうか。指定されていない場合、デフォルト値は false
phoneNumber 文字列 ユーザーのメインの電話番号
password 文字列 ユーザーのハッシュ解除された未加工のパスワード。6 文字以上
displayName 文字列 ユーザーの表示名
photoURL 文字列 ユーザーの写真 URL
disabled ブール値 ユーザーが無効かどうか。無効の場合は true、有効の場合は false (デフォルト)

20
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
12