react-firebase-hooksを使ってみた（Auth Hooks編）

## はじめに

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します。

```javascript
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の初期化をします。

```javascript
const firebaseConfig = {
  apiKey: "...",
  authDomain: "...",
  databaseURL: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "..."
};
firebase.initializeApp(firebaseConfig);
```

### Loginコンポーネント

ログイン用のコンポーネントを作ります。

```jsx
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コンポーネント

ログアウト用のコンポーネントを作ります。

```jsx
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です。

```jsx
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が必要になりそうです。