採用はこちら!

Shinonome Tech Blog

株式会社Shinonomeの技術ブログ
4 min read

FirebaseとReactによる独立した認証管理(下)

Firebase Authenticationの認証情報を、useContextを用いてリアルタイムでアプリ全体に伝搬させる方法について紹介します。

はじめに

バックエンドの藤岡です。

今回は前回の「FirebaseとReactによる独立した認証管理(上)」の続きです。前回Firebaseから取得した認証情報とユーザー情報をグローバルステートとしてアプリ内に共有させていく方法を紹介します。

なお、この方法はあくまで私が色々試してきた中で良さそうと感じたものですので、その点予めご了承下さい。

前提知識

  • Firebase Authenticationの基礎知識
  • Firestoreの基礎知識
  • Reactの基礎知識

前提条件

  • Reactアプリの初期化
  • Firebaseの初期化
  • Firebase Authentication、FirestoreがReactアプリ内で使える状態にある
  • React v.18.2.0
  • Firebase v.9.10.0
  • Typescript v.4.8.2

本ドキュメントでは、React、Firebaseの基礎的な操作はある程度できることを前提で進めていきます。その点予めご了承下さい。Firebase AuthenticationやFirestoreのセットアップはこちらの公式ドキュメントをご参照下さい。

Firebase Authentication
Firebase Authentication lets you add an end-to-end identity solution to your app for easy user authentication, sign-in, and onboarding in just a few lines of code.
Get started with Cloud Firestore | Firebase

なお、本ドキュメントのsrcディレクトリは以下のようなディレクトリ構成を想定しています。

src
├── App.tsx
│
├── components
│   └── Main.tsx
│
├── contexts
│   └── authContext.tsx
│
├── firebase
│   ├── emulator.ts
│   └── firebase.ts
│
├── function
│   └── validateUserType.ts
│
├── hook
│   ├── useAuthStatus.tsx
│   ├── useInitUser.tsx
│   ├── useObserveUser.tsx
│   └── useObserveUserDoc.tsx
│
├── index.tsx
│
└── logo.svg

おさらい

全体概要

今回の全体の概要は以下のようになっています。なお、紺色はコンポーネント、黄色はフックとなっています。

全体の流れとしては以下の通りです。

  1. Firebase AuthenticationやFirestoreから認証情報やユーザー情報を取得
  2. 取得した情報をグローバルステートに提供
  3. グローバルステートを共有するコンポーネントに提供

Firebase Authenticationから取得する認証情報やFirestoreから取得するスナップショットはリアルタイムで反映されるため、グローバルステートで取得する情報は常にリアルタイムの情報を反映することができます。

また、グローバルステートの取得をフック化することで、提供されているコンポーネント配下では、任意の場所でグローバルステートを呼び出すことができます。

今回は図の②と③の範囲を紹介します。それでは実際のコードを見ていきます。

グローバルステートを作成する

前回でFirebaseから User | null 型の認証情報と、 DocumentData | null 型のユーザー情報のFirestoreドキュメントを取得できるフックを作成しました。

const { user, userDocData, error } = useInitUser();

次はこのフックの返り値をグローバルステートに入れます。グローバルステートには useContext を使用します。(参考:公式ドキュメント)

useContext
A JavaScript library for building user interfaces

useContextの使い方

ここで、軽く useContext の使い方を振り返ります。

まず、全体的な流れとしては以下のようになります。(参考)

  • const Context = createContext(someContext) でコンテクストを作成します。
  • <Context.Provider value=<value>> でラップした子コンポーネント内で、作成したコンテクストを value で呼び出せます。
  • コンテクストの呼び出しには useContext フックを使用します。
[React] useContextの使い方を改めて確認してみた | DevelopersIO
こんにちは、CX事業本部 IoT事業部の若槻です。 ReactでGlobalなデータ共有を実現できるライブラリはいくつかあるのですが、React hooksで既定で使えるuseContextでも同様のことは行なえます。 …

使い方を復習ところで、認証情報を共有するフックを作っていきます。

認証情報を共有するフックを作成する

まず、 createContext で共有するコンテクストを作成します。

const AuthContext = createContext<{
  user: User | null;
  userDocData: DocumentData | null;
  error: any;
}>({
  user: null,
  userDocData: null,
  error: null;
});

デフォルト値はすべて null にしておきます。これでコンテクストが作成されました。

次に、 AuthContext コンポーネントがラップした子コンポーネント全体で使えるようなコンポーネントを作成します。

const AuthProvider: FC<{ children: ReactNode }> = ({ children }) => {
  const { user, userDocData, error } = useInitUser();

  return (
    <AuthContext.Provider
      value={{
        user,
        userDocData,
        error,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

これで、AuthContext コンポーネントでラップしたコンポーネント全体でコンテクストが使えるようになりました。今回はアプリ全体で使用したいので、 Main コンポーネントをラップしておきます。

// ./src/app.tsx

export function App() {
  return (
      <AuthProvider>
        <Main />
      </AuthProvider>
  );
}

最後に、登録したコンポーネントを取得する方法を少し楽にしておきます。 useContext(AuthContext) を引数無しで呼び出せるようにします。こうすることにより、 AuthContext をエクスポートしないで済むので、多少は安全になります。

export const useAuthContext = () => useContext(AuthContext);

最後にコード全体は以下のようになります。

// ./src/context/authContext.tsx

import { FC, ReactNode, createContext, useContext } from "react";
import { User } from "firebase/auth";
import useInitUser, { AuthStatus } from "../hook/useInitUser";
import { DocumentData } from "firebase/firestore";

const AuthContext = createContext<{
  user: User | null;
  userDocData: DocumentData | null;
  error: any;
}>({
  user: null,
  userDocData: null,
  error: null;
});

const AuthProvider: FC<{ children: ReactNode }> = ({ children }) => {
  const { user, userDocData, error } = useInitUser();

  return (
    <AuthContext.Provider
      value={{
        user,
        userDocData,
        error,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export const useAuthContext = () => useContext(AuthContext);

コンテクストを取得するときは以下のように useAuthContext() を呼び出すだけです。

// ./src/components/Main.tsx
  
const Main = () => {
  const { user, userDocData } = useAuthContext();
  
  // ...
}

これで認証情報を自由に呼び出せるようになりました。今回はメインとなるコンポーネントは Main コンポーネントのみでしたが、勿論このMain コンポーネントが階層構造になっていても、下位コンポーネントでこれらを呼び出すことができます。

まとめ

今回はFirebase AuthenticationとFirestoreを使って認証管理をしている場合のReactでの認証管理について紹介させていただきました。今回紹介させていただいた認証管理は、Firebaseのリアルタイムのデータ提供と、Reactの仮想DOMの特徴があってこそのものですので、他のBaaSの認証システムでは機能しない可能性がありますのでご注意下さい。最後に、この記事がなにかの参考になれば幸いです。