ReactFire v2 alphaを試してみる（Auth編）

## はじめに

[前回](https://qiita.com/daishi/items/13379bbce59f275f1d0c)の[記事](https://qiita.com/daishi/items/8b08b32185a9b050637a)では、react-firebase-hooksを試したのですが、12月6日の[React Day Berlin](https://reactday.berlin)では、ReactFireのプレゼンがありました。こちらはとても先進的で期待できます。

本記事では、Authの部分を移植してみたいと思います。

## ReactFireとは

ReactFire v1は2016年5月のリリースを最後に、その後deprecateされ、Firebase JS SDKを直接使うようアナウンスされています。それが[2019年7月よりv2として](https://github.com/FirebaseExtended/reactfire/issues/135)再度開発しているようです。

<https://github.com/FirebaseExtended/reactfire>

現在、masterブランチがv2になっています。

## コーディング

```jsx
import React, { Suspense, useState, useRef, useEffect } from "react";
import ReactDOM from "react-dom";

import "firebase/auth";
import { FirebaseAppProvider, useAuth, useUser } from "reactfire";

const firebaseConfig = {
  apiKey: "...",
  authDomain: "...",
  databaseURL: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "..."
};

const Login = () => {
  const auth = useAuth();
  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 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>
  );
};

const Logout = () => {
  const auth = useAuth();
  const [pending, setPending] = useState(false);
  const mounted = useRef(true);
  useEffect(() => {
    const cleanup = () => {
      mounted.current = false;
    };
    return cleanup;
  }, []);
  const logout = async () => {
    setPending(true);
    await auth().signOut();
    if (mounted.current) setPending(false);
  };
  return (
    <div>
      <button type="button" onClick={logout}>
        Logout
      </button>
      {pending && "Pending..."}
    </div>
  );
};

const Main = () => {
  const user = useUser();
  if (!user) {
    return <Login />;
  }
  return (
    <div>
      User: {user.email}
      <Logout />
    </div>
  );
};

const App = () => {
  return (
    <FirebaseAppProvider firebaseConfig={firebaseConfig}>
      <Suspense fallback={<div>Loading...</div>}>
        <Main />
      </Suspense>
    </FirebaseAppProvider>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
```

動きました。nextではだめで、canaryを使う必要がありました。

## おわりに

結果的には、前回のコードとそれほど変わっていないのですが、`useUser`があるので、自分でcontextを用意しなくていい分は楽になるかもしれません。Suspenseでローディング処理を書かなくていいのも楽ではあります。