Firebaseの認証機能の中に、現在ログインしているユーザーの情報を取得する関数にonAuthStateChanged()という関数があります。
ちょっと、使い方に癖があるので、備忘録的に記事にしました。
ちなみに、この処理を使っているソースとアプリはこちらです。
※ ソースはこちら:Github
※ アプリはこちら:「Firebaseに認証するだけのアプリ」
onAuthStateChanged()とは
onAuthStateChanged()とは、
といったところです。
onAuthStateChanged()の使いどころ
// onAuthStateChanged()を使っているサンプルコードの全体
import { onAuthStateChanged } from "firebase/auth";
import { createContext, useContext, useEffect, useState } from "react";
import { auth } from "../service/firebase/firebase";
const AuthContext = createContext();
export const useAuthContext = () => useContext(AuthContext);
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState("");
const [loading, setLoading] = useState(true);
const value = {
user,
loading,
};
useEffect(() => {
const unsubscribed = onAuthStateChanged(auth, (user) => {
setUser(user);
setLoading(false);
});
return () => {
unsubscribed();
};
}, []);
return (
<>
{loading ? (<p>しばらくお待ちください...</p>) :
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
}
</>
);
};
export { AuthContext, AuthProvider };
onAuthStateChanged()は、Firebaseの認証を必要とするシステムのContextに使う場合が多いんじゃないかと思います。
useEffect(() => {~~
useEffect(() => {
const unsubscribed = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
setLoading(false);
});
アプリが起動すると、ContextのコンポーネントのuseEffect()のコールバックでonAuthStateChanged()を使って、ユーザー情報を取得する。
useEffectというのが、解りにくいReact Hookです。useEffectに関しては、こちらを参照してみてください。
私としては、とりあえず、「コンポーネントが複数回レンダリングされても、一回だけしか実行されない処理」と理解したことがあります(第二引数の依存配列が指定されていないときなんですけど)。
そして、onAuthStateChanged()の第二引数のコールバックの引数がuser情報となり、コールバック中のsetCurrentUser(user)で、ユーザー情報を確保します。
さらに、ユーザーの状態が変化する度にsetCurrentUser(user)が実行され、AuthProviderコンポーネントがレンダリングされるということです。
そして、取得したユーザー情報をContextを経由してアプリ全体で使用するということです。
setLoading(false)は、ユーザー情報が確保できたということをフラグを立てているという感じの処理です。
return () => {
unsubscribed();
};
onAuthStateChanged()のretuenには、onAuthStateChanged()のユーザーの状態管理の監視を終了するための関数(クリーンアップ関数)が返されます。
それを、useEffectのreturnにコールバックとして渡して、ユーザー状態の監視を終了させます。
return (~~
return (
<>
{loading ? (<p>しばらくお待ちください...</p>) :
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
}
</>
);
useEffectは非同期に実行されるため、AuthProviderコンポーネントがレンダリングされると、onAuthStateChanged()の実行を待たずに、レンダリングが完了してしまいます。
そこで、
const [loading, setLoading] = useState(true);
と、loadingの初期値をtrueにしておいて、loadingがtrueの間は、
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
を表示せず、「しばらくお待ちください…」と表示させます。
そして、onAuthStateChanged()の処理が終了すると、
setCurrentUser(user);
setLoading(false);
とStateが更新され、loadingがfalseなり、AuthProviderコンポーネントがレンダリングされて、
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
が表示されます。
onAuthStateChanged()はauthの初期化もやっている
onAuthStateChanged()のコールバックでuser情報を求められるということも大切なのですが、onAuthStateChanged()は、authの初期化も行っています。
Firebaseのいろいろな認証を行うときには、authの参照をしばしば行います。
ですので、onAuthStateChanged()が終了していないと、authを参照できないという問題が発生します。
「useEffectは非同期に実行される」ということに注意
あるアプリのコードでは、onAuthStateChanged()を以下のように使用していました。
useEffect(() => {
onAuthStateChanged(auth, setCurrentUser);
});
このコードだと、authの初期化も、user情報の設定も終了していないままに、処理が進んでしまいます。
具体的に言うと、アプリにユーザーがログインしているか、いないかを判断するためにユーザー情報を確保しない前に、判断処理が入ってしまうことがあります。
そうすると、ユーザーがログインしてるにもかかわらず、ユーザーのログインする画面を出力してしまうなどの不具合が出ます。
また、ユーザー情報の領域を確保する前にユーザー情報を参照して、アプリがエラーになることもあります。
そのため、ユーザー情報を参照する前に、「ユーザ情報が未作成たは不完全ではないか」の判定を入れていました。
それよりは、onAuthStateChanged()の完了を待って、ユーザー情報を確保してから処理を進めた方がいいと思います。
最後に
Firebaseって、
これから、自分でアプリを作っていくのにどんどん使っていきたいなぁ、と思っている次第です。
ということで、あとあと、不良を作りこまないために、ちょっと気になったonAuthStateChanged()周りのことに関して記事にしてみました。
あと、onAuthStateChanged()がエラーになった時の考慮がされていません。
onAuthStateChanged()の第三引数に、エラーに関してのコールバックを指定できるようなのですが、今回はエラーの検証までできませんでした。
onAuthStateChanged()のエラーの検証を行ったら、記事に追加したいと思います。