Firebase SDK v9、RecoilでNext.jsアプリのGoogleログインを実装する
はじめに
この記事ではTypeScriptで書かれたNextアプリにFirebase JavaScript SDK v9を利用してGoogleログインを実装する方法について書きます。
認証するユーザはRecoilで管理します。
Context APIで管理する方法はこちらの記事で書いています。
また、前述した記事に載っていますので、この記事ではFirebaseプロジェクトの作成、Googleプロバイダの有効化などの詳しい手順は省略させて頂きます。
この記事が他の人の参考になれば幸いです。
また、この記事の内容に間違った記載がありましたら、指摘してもらえるとありがたいです。
環境
名前 | バージョン |
---|---|
macOS Big Sur | 11.6 |
Node.js | 16.9.0 |
TypeScript | 4.4.3 |
React | 17.0.2 |
Next.js | 11.1.2 |
Recoil | 0.4.1 |
Firebase JavaScript SDK | 9.0.2 |
事前準備
NextアプリにGoogleログインを実装するコードを書いていく前にFirebaseプロジェクト等を作る必要があります。
主に以下のことを事前に行う必要があります。
- Firebaseプロジェクトの作成
- Authentication、Googleプロバイダの有効化
- Webアプリの追加とその設定情報のメモ
詳しくはこちらの記事で行なっていますので、必要に応じて参照してください。
環境変数ファイルを作成する
Firebaseの設定情報はコード上に記述し、バージョン管理したくないので環境変数ファイル.env.local
を作成し、事前準備で得た設定情報を記述しておきます。
NEXT_PUBLIC_FIREBASE_API_KEY=<apiKey>
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=<authDomain>
NEXT_PUBLIC_FIREBASE_PROJECT_ID=<projectId>
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=<storageBucket>
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=<messagingSenderId>
NEXT_PUBLIC_FIREBASE_APP_ID=<appId>
firebaseライブラリのインストールとセットアップ
firebaseライブラリをインストールし、そのセットアップを行います。
npm i firebase
インストール後、任意のファイル(この記事ではlib/firebase.ts
)でfirebaseライブラリのセットアップを行います。
環境変数ファイル.env.local
からFirebaseの設定情報を読み込み、firebaseライブラリの初期化をしています。
また、app
は他のファイルで使用するのでエクスポートしています。
import { initializeApp } from "firebase/app";
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};
export const app = initializeApp(firebaseConfig);
参考:Add Firebase to your JavaScript project
ログイン、ログアウトを行う関数を定義する
任意のファイル(この記事ではlib/auth.ts
)にログイン、ログアウトを行う関数を定義します。lib/auth.ts
に以下のように記述しました。
import { getAuth, signInWithRedirect, signOut, GoogleAuthProvider } from "firebase/auth";
import { app } from "./firebase";
export const login = (): Promise<void> => {
const provider = new GoogleAuthProvider();
const auth = getAuth(app);
return signInWithRedirect(auth, provider);
};
export const logout = (): Promise<void> => {
const auth = getAuth(app);
return signOut(auth);
};
signInWithRedirect()
でGoogleログイン用のページにリダイレクトし、Googleログインを行います。
また、provider、authのプロパティを変更することで言語などを変更、設定できます。
詳しくはAuthenticate Using Google Sign-In with JavaScriptなど参照してください。
signOut()
でログアウトします。
ユーザ認証を管理する関数を定義する
Recoilでユーザ認証を管理します。lib/auth.ts
に以下のように追記しました。
import { useEffect, useState } from "react";
import { atom, useRecoilValue, useSetRecoilState } from "recoil";
import {
User,
getAuth,
signInWithRedirect,
signOut,
onAuthStateChanged,
GoogleAuthProvider,
} from "firebase/auth";
import { app } from "./firebase";
type UserState = User | null;
const userState = atom<UserState>({
key: "userState",
default: null,
dangerouslyAllowMutability: true,
});
export const login = (): Promise<void> => {
const provider = new GoogleAuthProvider();
const auth = getAuth(app);
return signInWithRedirect(auth, provider);
};
export const logout = (): Promise<void> => {
const auth = getAuth(app);
return signOut(auth);
};
export const useAuth = (): boolean => {
const [isLoading, setIsLoading] = useState(true);
const setUser = useSetRecoilState(userState);
useEffect(() => {
const auth = getAuth(app);
return onAuthStateChanged(auth, (user) => {
setUser(user);
setIsLoading(false);
});
}, [setUser]);
return isLoading;
};
export const useUser = (): UserState => {
return useRecoilValue(userState);
};
userState
は認証するユーザを保持するRecoilの状態です。
ユーザがログインしている場合はUser
、ログインしていない場合はnull
になります。useUser()
はそのuserStateを他のコンポーネントで呼び出すための関数です。
useAuth()
がユーザ認証を監視するための関数です。
onAuthStateChanged()
メソッドはユーザ認証を監視し、変更があったときに引数のコールバック関数を実行します。
その関数内でユーザの状態を更新しています。
isLoading
はonAuthStateChanged()
を実行中か確認するための状態です。この状態の値がtrueの時はonAuthStateChanged()
でユーザを認証中です。
ログイン、ログアウト、ユーザ認証の管理を実装する
上記で定義した関数を使用し、実際にユーザ認証を実装します。
まず、pages/_app.tsx
に以下のように追記しました。
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { RecoilRoot } from "recoil";
import { useAuth } from "../lib/auth";
type Props = {
children: JSX.Element;
};
const Auth = ({ children }: Props): JSX.Element => {
const isLoading = useAuth();
return isLoading ? <p>Loading...</p> : children;
};
function MyApp({ Component, pageProps }: AppProps) {
return (
<RecoilRoot>
<Auth>
<Component {...pageProps} />
</Auth>
</RecoilRoot>
);
}
export default MyApp;
useAuth()
は<RecoilRoot>
の子孫コンポーネントでしか使用できないので<Auth>
コンポーネントを作成し、その中で使用しています。
ユーザ認証中は<p>Loading...</p>
のみが表示されます。
ログイン、ログアウトは以下のように任意の場所で関数を呼び出し、行ないます。
また、useUser()
も以下のように使用し、ユーザを取得できます。
...
import { useUser, login, logout } from "../lib/auth";
const Home: NextPage = () => {
const user = useUser();
const handleLogin = (): void => {
login().catch((error) => console.error(error));
};
const handleLogout = (): void => {
logout().catch((error) => console.error(error));
};
return (
<div className={styles.container}>
<Head>
<title>Auth Example</title>
</Head>
<div>
<h1>Auth Example</h1>
{user !== null ? (
<h2>ログインしている</h2>
) : (
<h2>ログインしていない</h2>
)}
<button onClick={handleLogin}>ログイン</button>
<button onClick={handleLogout}>ログアウト</button>
</div>
</div>
);
};
...
Discussion
どんぴしゃな記事で助かります!意外とfirebase v9の記事は少なく。
・・・しかし、Recoil新しすぎて、手出しにくいです~、、
出来れば、Reduxか、React Hooksを使ったVersionも別記事か本記事の更新版なのか、公開してほしいです!