はじめに
FirebaseとFirestoreをReact Hooksで使いたいと以前から思っていましたが、react-firebase-hooks v1はあまり納得がいかず、自作のcustom hooksを使っていました。その後v2が出たので、調べなければと思いつつ、半年くらい経ってしまいましたが、とうとう重い腰をあげることにします。
react-firebase-hooks
リポジトリはこちらです。
https://github.com/CSFrequency/react-firebase-hooks
今回はAuth Hooksを試してみようと思います。
コーディング
モジュールのimport
最初に必要なモジュールをimportします。
import React, { useState, useRef, useEffect } from "react";
import ReactDOM from "react-dom";
import firebase from "firebase";
import { useAuthState } from "react-firebase-hooks/auth";
firebaseの初期化
次に、firebaseの初期化をします。
const firebaseConfig = {
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "..."
};
firebase.initializeApp(firebaseConfig);
Loginコンポーネント
ログイン用のコンポーネントを作ります。
const Login = () => {
const [email, setEmail] = useState("");
const [pass, setPass] = useState("");
const [error, setError] = useState(null);
const [pending, setPending] = useState(false);
const mounted = useRef(true);
useEffect(() => {
const cleanup = () => {
mounted.current = false;
};
return cleanup;
}, []);
const onSubmit = async e => {
e.preventDefault();
setError(null);
setPending(true);
try {
await firebase.auth().signInWithEmailAndPassword(email, pass);
} catch (e) {
console.log(e.message, mounted);
if (mounted.current) setError(e);
} finally {
if (mounted.current) setPending(false);
}
};
return (
<div>
<form onSubmit={onSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email..."
/>
<input
type="password"
value={pass}
onChange={e => setPass(e.target.value)}
placeholder="Password..."
/>
<button type="submit">Login</button>
{pending && "Pending..."}
{error && `Error: ${error.message}`}
</form>
</div>
);
};
ちょっと複雑になりましたが、やっていることは単純です。本来は、テキストフィールドを更新したところで、エラーメッセージをクリアすべきですが、そこは省略。
Logoutコンポーネント
ログアウト用のコンポーネントを作ります。
const Logout = () => {
const [pending, setPending] = useState(false);
const mounted = useRef(true);
useEffect(() => {
const cleanup = () => {
mounted.current = false;
};
return cleanup;
}, []);
const logout = async () => {
setPending(true);
await firebase.auth().signOut();
if (mounted.current) setPending(false);
};
return (
<div>
<button type="button" onClick={logout}>
Logout
</button>
{pending && "Pending..."}
</div>
);
};
Pending表示が短い場合はChrome Dev ToolsのNetwork TabでThrottlingをしましょう。
Appコンポーネント
最後に、全体をつなげるAppコンポーネントとReactDOMのrenderです。
const App = () => {
const [user, initialising, error] = useAuthState(firebase.auth());
if (initialising) {
return <div>Initialising...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
if (!user) {
return <Login />;
}
return (
<div>
User: {user.email}
<Logout />
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
今回は、ログインしたらユーザのemailを表示するだけのシンプルなものです。
CodeSandbox
https://codesandbox.io/s/upbeat-chaum-vzpjg
完成したものがこちらです、実際に動作させるためにはforkしてfirebaseConfig
を置き換える必要がありますのでご注意ください。
おわりに
今までは、onAuthStateChangedをラップした独自custom hooksを使ってましたが、それがライブラリ化されることで、多少見通しはよくなったような気はします。しかし、loginやlogoutの機能を内包するcustom hooksは提供されていないため、今回のように長いコードになってしまいました。結局、そこには独自custom hooksが必要になりそうです。