Firebase＋Reactで会員登録機能を作る際に、ユーザー名(username)も一緒に登録したい！

`Firebase Auth react`や`firebase react`で検索すると、基本的な`email+passwordでユーザー登録する機能の作り方`は出くるのですが、ユーザー名も一緒に登録する方法を調べていても一向にしっくりくる情報が得られないんですよね。。

そこで、この記事では`firebase+reactでユーザー登録の機能を作る際に、ユーザー名もセットで登録する方法`について解説します。

## 実装の全体像
まずは全体像を掴みましょう。

`firebaseでユーザー登録`といえば、Authenticationですよね。以下の画像のように、様々な認証方法があってとても便利な機能です。
![スクリーンショット 2020-11-03 21.46.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509690/cd9c391f-af72-a264-1a83-d3189dc0aa1b.png)


![スクリーンショット 2020-11-03 21.49.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509690/dd73091d-7319-e0b2-9732-c553dff0d372.png)

ただ、初心者の僕はここで疑問が浮かびます。

***「Authenticationでメールアドレスとパスワード、ユーザーのIdが管理できるのはわかった。だけど、ユーザー名を登録したいと思ったらどうすれば良いんだろう？🤔」***

僕はこの疑問についてずっと考えたのですが、良い答えに辿り着けず数日が経過したある日、ふと思いました。

***「もしかしたら、Firestore使えばいけるんじゃね？」***

そうです。**firebaseでユーザー名を登録する場合はAuthenticationではなくfirestoreにデータを保存する必要があった**のです。

この`Authenticationでどうすればユーザー名登録できるか`みたいな考えに束縛されて、答えに辿り着けずにいました。

### 具体的にどうすれば良いのか

前述の通り、`firestore`に`db.collection('users').doc(uid).set(userInitialData)`を使って`username`、`email`、`password`のデータを保存します。(後述します)

## ユーザーを追加する方法を解説

仮に、`email`と`password`だけでユーザー登録するとしたら、特にfirestoreを使う必要はなく、`createUserWithEmailAndPassword`でできるのですが、`ユーザー名(username)`を登録するとなると、firestoreを使う必要があります。

まずは、お好みの形でFormを作成してください。

僕の場合は、Bootstrapを使っているので、`SignUp.jsx`ファイルは以下のようになりました。
今回特にみて欲しいのは`hundlesubmit`の部分です。

```SignUp.jsx
import React, { useRef, useState } from "react"
import { Form, Button, Card, Alert } from "react-bootstrap"
import { useAuth } from "../contexts/AuthContext"
import { Link, useHistory } from "react-router-dom"

export default function Signup() {
 // ※長くなりすぎるので、一部省略しています
  const [error, setError] = useState("")
  const [loading, setLoading] = useState(false)
  const history = useHistory()

//特にみて欲しいのはここ
  async function handleSubmit(e) {
    e.preventDefault()

    //パスワードの一致値チェック
    if (passwordRef.current.value !== passwordConfirmRef.current.value) {
      return setError("パスワードが一致しません")
    }

　　//fromで送られてきた値を処理する
    try {
      setError("")
      setLoading(true)
　　　　//signUpという別のコンポーネントに記述した関数に引数として、fromの値を渡し、実行させる
      await signup( usernameRef.current.value, emailRef.current.value, passwordRef.current.value)
      history.push("/")
    } catch {
      setError("アカウントの作成に失敗しました")
    }

    setLoading(false)
  }

  return (
    <>
      <Card>
        <Card.Body>
          <h2 className="text-center mb-4">サインアップ</h2>
          {error && <Alert variant="danger">{error}</Alert>}
          <Form onSubmit={handleSubmit}>
            <Form.Group id="username">
              <Form.Label>名前</Form.Label>
              <Form.Control type="text" ref={usernameRef} required />
            </Form.Group>
            <Form.Group id="email">
              <Form.Label>Email</Form.Label>
              <Form.Control type="email" ref={emailRef} required />
            </Form.Group>
            <Form.Group id="password">
              <Form.Label>パスワード</Form.Label>
              <Form.Control type="password" ref={passwordRef} required />
            </Form.Group>
            <Form.Group id="password-confirm">
              <Form.Label>パスワードの確認</Form.Label>
              <Form.Control type="password" ref={passwordConfirmRef} required />
            </Form.Group>
            <Button disabled={loading} className="w-100" type="submit">
              登録する
            </Button>
          </Form>
        </Card.Body>
      </Card>
    </>
  )
}

```
以下のファイルにSignUpの挙動を示す関数を記述します。

```AuthContext.jsx
//省略しています  

　//formが値を送信するとこのSignUp関数が実行される
  function signup(username, email, password) {
    //createUserWithEmailAndPassword(これはfirebaseのメソッド)でユーザーを作成
    return auth.createUserWithEmailAndPassword(email, password)
　　　　//ユーザーを作成したら
      .then(result => {
　　　　　//userが戻り値として返ってくるので、それをuserに代入
        const user = result.user
　　　　　//userに値が入っていれば
        if(user) {
          //userの中にあるuidをuidに代入
          const uid = user.uid
          //他にも持っている値でfirestoreに登録したいものをuserInitialDataに代入
          const userInitialData = {
            email: email,
            uid: uid,
            username: username
          }

       //firestoreのusersというコレクションに、uidをドキュメントIDとしてもつ、 userInitialDataを登録する
       firebase.firestore().collection('users').doc(uid).set(userInitialData)
            .then('ユーザーが作成されました!')
        }
      })
  }

//以下省略

```

`auth.createUserWithEmailAndPassword(email, password)`のauthはfirebaseの初期設定ファイルで、`app.auth()`を定数化しています。

```firebase.jsx

const app = firebase.initializeApp({
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID
})

export const auth = app.auth()
export default app
```
ちなみに、`app`の部分は直接このファイルに書き込むのではなく、別に用意した`.env`ファイルに記述して、.gitignoreしています。

その方法は[こちらの記事](https://qiita.com/KOBA-RYOTA/items/f62bb0e0f422092e5fff)で説明しています。

`AuthContext.jsx`で使っている`createUserWithEmailAndPassword`メソッドは戻り値が`Promise < UserCredential >`となっています。[UserCredential](https://firebase.google.com/docs/reference/js/firebase.auth?hl=ja#usercredential)はemailやuidなど、他にも様々なデータを格納しています。

今回は、そこから`user`の中に入っている`email`、`username`、`uid`を取り出しました。


(createUserWithEmailAndPasswordはfirebaseの[公式のリファレンス(英語)](https://firebase.google.com/docs/reference/js/firebase.auth.Auth?hl=ja#createuserwithemailandpassword)にメソッドの返り値や引数などの詳細が載っています。)


そして、`firebase.firestore().collection('users').doc(uid).set(userInitialData)`でコレクションにデータを保存します。
繰り返しになりますが、`users`というコレクションの、`uid`をIDにもつドキュメントに、先ほど取得した`userInitialData`を`.set()`メソッドで登録します。

これで完了です🙌🙌

## 参考 
[Firebase API Reference](https://firebase.google.com/docs/reference?hl=ja)