Firebase with hooks
ログインステータスやユーザー情報はグローバルステートとして持っていた方が便利。
かつてはReduxで管理したものだけど、今はReduxを導入する場合はかなり少なくなり、Hooksが台頭している。
今回はFirebaseのユーザー認証をContext APIとHooksで管理する。
作るものは主に以下の3つだけ
- initFirebase.js
- useFirebase.js
- authProvider.js
1. initFirebase.js
initFirebase.js
はその名のとおり、Firebaseを初期化してexportするだけのjsファイル。
Firebaseが初期化されていない場合だけ動作するようにしておかないと、意図せず何度も初期化してしまうので注意。
Gatsbyの場合はtypeof window !== 'undefined'
を使ってブラウザ上でのみ動作するように制限する。
こうしておかないとBuild時にエラーになってしまう。
// initFirebase.js
import firebase from 'firebase/app'
import 'firebase/auth'
const firebaseConfig = {
apiKey: '************',
authDomain: '************',
projectId: '************',
storageBucket: '************',
messagingSenderId: '************',
appId: '************',
}
let firebaseInstance
export const initFirebase = () => {
if (typeof window !== 'undefined') {
firebaseInstance ??= firebase.initializeApp(firebaseConfig)
}
return firebaseInstance
}
2. useFirebase.js
useFirebase.js
では初期化したFirebaseをstateとしてexportする。
アプリ内でfirebase
を使いたい時はこのhookを通して使う。
// useFirebase.js
import { useState, useEffect } from 'react'
import { initFirebase } from '.initFirebase'
export const useFirebase = () => {
const [firebase, setFirebase] = useState(null)
useEffect(() => {
setFirebase(initFirebase())
}, [])
return { firebase }
}
3. authProvider.js
最後にContext providerを作る。
onAuthStateChanged()
を使うことでユーザーのステータスの変化を検知することができる。
これを使ってstateのuserInfo
を更新する。
さらにonAuthStateChanged()
はfirebase.Unsubscribe
を返すので、ComponentがUnmountされるタイミングでUnsubscribeしておく。
firebase
は最初null
の可能性があるので、useEffectの第2引数で[firebase]
を指定しておくのを忘れずに。こうしておけばfirebase
の中身が入ったタイミングでもう一度useEffect
が走る。
initializing
のフラッグは無くてもいいが、あると便利。
// authProvider.js
import React, { useState, createContext, useEffect, useMemo } from 'react'
import { useFirebase } from './useFirebase'
const initialValue = {
userInfo: null,
initializing: true,
}
export const AuthContext = createContext(initialValue)
export const AuthContextProvider = ({ children }) => {
const { firebase } = useFirebase()
const [initializing, setInitializing] = useState(true)
const [userInfo, setUserInfo] = useState(null)
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
setUserInfo(user || null)
setInitializing(false)
})
return () => unsubscribe()
}, [firebase])
const value = useMemo(
() => ({
userInfo,
initializing,
}),
[userInfo, initializing]
)
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
あとは作ったContextProviderでアプリ全体を包めば完成。
// index.js
import React from 'react'
import { AuthContextProvider } from './authProvider'
import YourApp from './yourApp'
export default () => {
return (
<AuthContextProvider>
<YourApp />
</AuthContextProvider>
)
}
How to use
あとはuseContext()
を使えば好きな場所でユーザーの状態を取得できるし、その状態は常に更新される。
initializing
のフラッグがあればロード画面などを表示できるので、設定しておいても別に損はない。
import React, { useContext } from 'react'
import { AuthContext } from './authProvider'
import { Spinner } from './spinner'
export const Component = () => {
const { userInfo, initializing } = useContext(AuthContext)
if (initializing) return <Spinner />
return userInfo ? <Something /> : null
}