ReactにFirebaseでGoogleアカウントの情報を反映させる

## はじめに
Googleが提供しているBaasのサービスである`Firebase`を使えば、バックエンドの知識がなくてもユーザーの管理などができるようになります。
チャットアプリではどのような使い方ができるのかを考えて導入してみました。

## Firebaseを導入するアプリ
`Firebase`を反映させるのは以下のアプリ。

- URL: [Reactチャット](https://react-chat-b0e8a.firebaseapp.com/)
- Github: [kaibara/React-chat](https://github.com/kaibara/React-chat)

## Firebase編
### Firebaseの使い方
今回はFirebaseをユーザー管理に用いました。
具体的には次の操作をできるようにします。

- **Googleアカウントでログイン/ログアウトをする**
- **ログインしていればGoogleアカウントの名前でチャットにメッセージを送れる**
 
### Firebaseの導入方法
`Firebase`の導入方法についてはこちらをご覧ください。
[ReactにFirebaseを使ったログイン機能を実装する](https://qiita.com/kaitaku/items/6cfbcfed55cc6e817b20)

## 実践編
ここからはコードの記入、解説をしていきます。
下の記事で作成したチャットアプリにコードを追加していくため、Reactの前提知識やファイルの構成などが気になった方はご覧ください。
[Reactでのチャット作成手順を誰にでもわかるように解説する](https://qiita.com/kaitaku/items/1ddeabd5372bc635febd)

### Firebaseの設定
自分は`src/`ディレクトリ直下に`firebase/firebase.js`を設定しました。

```firebase/firebase.js
import firebase from 'firebase/app'
import 'firebase/auth'

const config = {
  apiKey: "***",
  authDomain: "***",
  databaseURL: "***",
  projectId: "***",
  storageBucket: "***",
  messagingSenderId: "***"
}

firebase.initializeApp(config)

export default firebase
```

ここでできるコードの解説はほとんどありません。
`Firebase console`からconfigの情報を持ってきて定義し、`initializeApp`で`firebase`の初期設定を行い、`export`で他のファイルから読み取れるようにしただけです。
わからないことがあれば、上の自分が作成した記事や`Firebase`のドキュメントを確認してみてください。

### ログインできるようにする
まずはこのファイルにGoogleアカウントでログインできるようにします。
ユーザーの名前は`src/components/ChatForm.js`で入力しているため、そこに以下のコードを追加します。

```src/components/ChatForm.js
 import firebase from '../firebase/firebase'

 constructor(props){
    super(props)
    this.state = {
      name: '',
      message: '',
      user: null
    }
  }

  componentDidMount(){
    firebase.auth().onAuthStateChanged(user => {
      this.setState({ user })
    })
  }
  //このcomponentがDOMにマウントされた直後、Googleにログインしている状態であればthis.state.userにfirebaseで管理しているuserの値を入れる。

  login() {
    const provider = new firebase.auth.GoogleAuthProvider()
    firebase.auth().signInWithRedirect(provider)
  }
  //ログイン処理をするイベントの設定。今回はGoogleを使用するためGoogleのProviderに接続してGoogleユーザーの情報でログインする。
  logout(){
    firebase.auth().signOut()
  }
  //ログアウト処理をするイベントの設定。

  {this.state.user ?
     <button onClick={this.logout}>Google Logout</button> :
     <button onClick={this.login}>Goggle Login</button> 
  }
  //ログイン、ログアウトするボタンの設定。
  //this.state.userに値が入っていればログアウト処理をするボタンを表示。
  //this.state.userに値が入っていなければログイン処理をするボタンを表示。
```

### ログインしていればGoogleアカウントの名前でチャットにメッセージを送れる
次に、ログインしていればGoogleアカウントの名前でチャットにメッセージを送れるようにします。
自分が採った解決法は、『ログイン時には2つのボタンが表示される。片方はその名前を使用するボタン、もう片方はログアウトするボタン』というものです。
私は`state`や`props`の遷移や情報の引渡しに関する知識が弱いため、ログインしただけでその名前を使用できるような方法があるかも知れません。
その方法を知っていたり、発見した際には教えていただきたいです。

```src/components/ChatForm.js

  nameChanged(e){
    this.setState({name: e.target.value})
  }

  {this.state.user ? (
    //ログインしているかどうかの確認

      <div className='login'>
        <h2>{this.state.user.displayName}</h2>
         //Googleアカウントで使用している名前を表示する
        <button value={this.state.user.displayName} onClick={e => this.nameChanged(e)}>この名前を使用する</button>
         //1つ目のボタン
         //このボタンを押すとthis.state.nameにGoogleアカウントで使用している名前が渡される
        <button onClick={this.logout}>違う名前を使用する</button>
         //2つ目のボタン
         //このボタンを押すとログアウトできる。
      </div>
      //ログインしている時の処理
      ) :(
      <input value={this.state.name} onChange={e => this.nameChanged(e)}/>
     //ログアウトしている時の処理
  )}
       
```

### 完成版ChatForm.js
最後に`ChatForm.js`のコードを一から載せます。

<details><div>

```src/components/ChatForm.js
import React,{Component} from 'react'
import socketio from 'socket.io-client'
import firebase from '../firebase/firebase'

const portNumber = process.env.PORT || 3005
const socket = socketio.connect('http://localhost:' + portNumber)

class ChatForm extends Component {
  constructor(props){
    super(props)
    this.state = {
      name: '',
      message: '',
      user: null
    }
  }

  componentDidMount(){
    firebase.auth().onAuthStateChanged(user => {
      this.setState({ user })
    })
  }
  login() {
    const provider = new firebase.auth.GoogleAuthProvider()
    firebase.auth().signInWithRedirect(provider)
  }
  logout(){
    firebase.auth().signOut()
  }
  nameChanged(e){
    this.setState({name: e.target.value})
  }
  messageChanged(e){
    this.setState({message: e.target.value})
  }
  send(){
    socket.emit('chatMessage',{
      name: this.state.name,
      message: this.state.message
    })
    this.setState({message: ''})
  }

  render(){
    return(
      <div id='Form'>
        <div className='Name'>
          名前:
          {this.state.user ? (
            <div className='login'>
              <h2>{this.state.user.displayName}</h2>
              <button value={this.state.user.displayName} onClick={e => this.nameChanged(e)}>この名前を使用する</button>
              <button onClick={this.logout}>違う名前を使用する</button>
            </div>
          ) :(
            <input value={this.state.name} onChange={e => this.nameChanged(e)}/>
          )}
          <br />
          {this.state.user ?
            null : <button onClick={this.login}>Goggle Login</button>
          }
          //ログイン時には2つ目のボタンでログアウトできるため、ここで実装する必要はない。そのためログインのみ実装
        </div>
        <br />
        <div className='Message'>
          メッセージ:
          <br />
          <input value={this.state.message} onChange={e => this.messageChanged(e)} />
        </div>
        <button className='send' onClick={e => this.send()}>送信</button>
      </div>
    )
  }
}

export default ChatForm
```

</div></details>

## 実際の動き
今回のコードを追加すると、このように動作します。
<img src="https://qiita-image-store.s3.amazonaws.com/0/218398/b5d8a0fa-9b4b-75ae-10e8-a3ce7d54dc7c.gif" width="700px">


## 最後に
`Firebase`を利用して、Reactで作成したチャットに機能を追加することができました。

しかし、利便性には乏しい問題があります。
1つ目のボタンを押してもちゃんと反映されているのかわかりにくかったり、余分がコードが多かったりすることを理解しています。
moduleや他のサービスを調べ、機能の追加だけでなく、利便性の向上も意識して勉強していきたいです。