Firebase認証とfirestoreの連携 – React

本稿では、firebaseで認証を経たユーザーの情報を、同じid(キー)を使ってfirestoreにも保存する方法をReactでご紹介します。firebaseの認証機能で保存できるユーザーの情報は、パスワードとidを除くと、ユーザー名、メールアドレス、photoURLに限られるので、それ以上のユーザーに紐づく情報を保存しようとするためにfirestoreを使用します。以下、firebase authentication(認証)とfirestoreについての基本的な理解がある前提で解説しますが、必要に応じて私の下記の記事も参考にしていただければ幸いです。

  1. React + Firebaseを使ったユーザー認証の実装方法
  2. メールとパスワードによるユーザー認証の実装方法 – Firebase + React
  3. React + Firestore【入門から実装まで】

連携の基本的な仕組み

Firebaseとfirestoreの連携は次の2ステップです。(1)firebase auth(認証)で、ユーザーのサインアップが完了すると自動で固有のuidが発行されるので、サインアップと同時に、(2)同uidをidに指定してfirestoreのドキュメントを新規作成します。Authで発行されたユーザーのuidを各ユーザーのドキュメントのidとしている点がポイントです。

Reactでの実装

下記はReactでの実装例です。ユーザー認証の部分はfirebaseでは様々な方法が提供されていますが、ここではGoogle Authを使った例をご紹介します。

SignUp.jsx

import { useNavigate } from "react-router-dom";
import {
getAuth,
signInWithPopup,
GoogleAuthProvider
} from "firebase/auth";
import { doc, setDoc, getDoc } from "firebase/firestore";
import { db } from "../firebase.config";

function SignUp() {
const navigate = useNavigate();
const onGoogleClick = async () => {
try {
const auth = getAuth();
const provider = new GoogleAuthProvider();
const result = await signInWithPopup(auth, provider);
const user = result.user;
const docRef = doc(db, "users", user.uid);
const docSnap = await getDoc(docRef);

if (!docSnap.exists()) {
await setDoc(doc(db, "users", user.uid), {
name: user.displayName,
email: user.email,
});
}
navigate("/");
} catch (error) {
console.log("Error");
}
};
return (
<div>
<button onClick={onGoogleClick}>
SignUp
</button>
</div>
);
}

export default SignUp;

import

useNavigateはReact Routerの関数です。サインアップ後のリダイレクトに使用します。React Routerの詳細については私のこちらの記事「Reactで複数のページを作る方法 – Router v6.0準拠」をご覧ください。リダイレクトが可能であれば他のライブラリー・方法でも問題ありません。

const navigate = useNavigate();

getAuth, signInWithPopup, GoogleAuthProviderの3つは、いずれもGoogle認証に使用する関数です。いずれも、前述の私の別の記事「メールとパスワードによるユーザー認証の実装方法 – Firebase + React」で解説しています。

import {
getAuth,
signInWithPopup,
GoogleAuthProvider
} from "firebase/auth";

doc, setDoc, getDocの3つは、firestoreの関数です。最後のdbはfirebaseのコンフィギュレーションファイルからインポートしたdbです。後ほど詳しく解説しますが、コンフィギュレーションファイルについては、前述の私の別の記事「React + Firestore【入門から実装まで】」をご覧ください。

import { doc, setDoc, getDoc } from "firebase/firestore";
import { db } from "../firebase.config";

useNavigate

useNavigateは、React Routerの機能でサインアップ後のリダイレクトに使用します。useNavigateを使用するとリダイレクト先のページをサーバーから再読み込みせず、Reactのレンダリング機能(仮想DOM機能)によってスムーズに差分更新されます。詳細は前述のReact Routeを記事をご覧ください。

const navigate = useNavigate();

onGoogleClick

onGoogleClickは、サインアップとデータベースへの登録をする関数です(名称は任意)。JXS内のボタンをクリックすると(onClick)実行されます。関数内でfirebaseのサインアップとfirestoreへの書き込みという2つの非同期処理(awaitの付いた処理)を行うので全体をasyncの関数とします。

const onGoogleClick = async () => {
try {

} catch (error) {

}
};

getAuth

getAuthはfirebaseのauthenticationと接続するためのオブジェクトを取得する関数です。コンフィギュレーションファイルで設定してエクスポートしても、本事例のようにコンポーネントで直接設定しても問題ありません。取得したauthは後ほどご紹介するsignInWithPopupで使用します。

const auth = getAuth();

GoogleAuthProvider()

Googleの認証(サインアップ)は、下記の1、2行目で完了します。1行目はサインアップの方法に関わらず共通です。2行目はサインアップの方法に応じて2種類あります。本事例で使用するsignInWithPopupは名前の通り、ポップアップを表示させてサインアップします。ポップアップではなく、ブラウザからGoogleのサインアップページを開いて認証したい場合は、signInWithRedirectを使用します。2つの引数は記載の通りです。

3行目ではサインアップの後に取得したオブジェクトからuserを取得します。このuser自体もオブジェクトで、firestoreへのデータを保存するためにします。

const provider = new GoogleAuthProvider();
const result = await signInWithPopup(auth, provider);
const user = result.user;

doc, getDoc

1行目のdocにより、ドキュメントへのレファレンス(参照)オブジェクトを作成します。1番目の引数dbは、Firebaseのコンフィギュレーションファイルで作成しエクスポートしたdbです。docの2番目の引数はコレクションの名称(=id)です(本事例では”users”)。docの3番目の引数は、ドキュメントを特定するためのidです。idに、authenticationから取得したuserオブジェクト内のuid(user.uid)で指定している点が重要です(本稿で最も重要な部分)。user.uid以外のものをidとするとfirebase認証のユーザー情報とfirestoreのユーザーに関するデータベースを結びつけることができなくなります。

2行目のgetDocはドキュメントが存在するか否かを確認するための関数で、1行目で作成したドキュメントへの参照オブジェクトを引数とします。得られたオブジェクト(本事例ではdocSnap)に対して、exists()メソッドがあり、データの有無を確認することが可能です。

const docRef = doc(db, "users", user.uid);
const docSnap = await getDoc(docRef);

setDoc

1人のユーザーにつき、firebaseの認証とfirestoreのドキュメントを1対1で対応させたいので、既にユーザーのドキュメントがfirestoreに存在する場合は、新たなドキュメントを作成しません。そのため、firestoreに保存するsetDoc全体をif文の内側に配置しています。if文には、先ほど説明したデータの有無を確認するコードを入力します。不存在の場合に限りドキュメントを新規作成するので、先頭にNot(!)が付けます。

setDocではコレクションの内側のドキュメントをidで指定し、その中に具体的なフィールドを作成します。ドキュメントの指定には、先ほどのdocで説明したものと同じオブジェクトを使用し、1番目の引数とします。2番目の引数はfirestoreのドキュメントに保存するフィールドのオブジェクトです。本事例ではfirebaseの認証から取得した名前(user.displayName)とemail(user.email)のみを保存していますが、ユーザーがフォームに入力した事項などもfirestoreに保存可能です。

if (!docSnap.exists()) {
await setDoc(doc(db, "users", user.uid), {
name: user.displayName,
email: user.email,
});
}

navigate

tryブロックの最後に配置しているnavigateは、サインアップ又はログインが成功した際のリダイレクトに使用します。本事例ではホーム(”/”)にリダイレクトします。

navigate("/");

エラー処理

サインアップ又はログインに失敗した場合は、catchブロックでエラーを処理します。本事例ではログに”Error”と表示していますが、ユーザーにエラーを表示させた場合はreactのtoastifyが便利です。詳しくは私のこちらの記事「React-toastifyの使い方」でご紹介しています。

今日も最後まで読んで頂きありがとうございました。本稿で紹介したコードはこちら「React Front To Back 2022」のudemyの講座を参考に作成いたしました。