Help us understand the problem. What is going on with this article?

Reactで作ったチャットのメッセージをFirebaseRealtimeDatabaseに追加、削除する

More than 1 year has passed since last update.

はじめに

ReactFirebaseを使用したチャットシステムを作成していました。
それまではFirebaseはログイン状態を管理するために使用していましたが、RealtimeDatabaseの操作を学びたかったため、メッセージの追加、削除も管理するようにしました。
その時に学んだことを記録します。

この記事のコードでできること


削除ボタンを押した際、更新してくださいというポップアップが表示されます。

現在の状態

実装

ファイル構造

今回のファイル構造はこのようになっています。

src ー App.js
   |ー components
   |      |ー ChatMEssage.js
   |      |ー ChatForm.js
   |
   |ー firebase
       |ー firebase.js

FirebaseRealtimeDatabase

まずはFirebaseRealtimeDatabaseに接続します。
アプリの名前の設定などができていない場合は、ReactにFirebaseでGoogleアカウントの情報を反映させるを参考にしてください。

Firebase console

Firebase consoleでアプリの名前が設定できたら、開発 -> Databaseを選択してください。
最初にCloud Firestoreが表示されますが、それではなく、その下にあるRealtime Databaseを選びましょう。

次に、データベースを作成を選択し、テストモードを選んでください。

firebase init

ここまでできたら端末の操作に移ります。

 $ firebase init
 // Database,Hostingを選択
 // 基本的に全部Enterで可能、カスタマイズしたい場合は各自で変更

上のコマンドの処理が終わったら、作成されたdatabase.rules.jsonを確認してください。

database.rules.json
{
  "rules": {
    ".read": "true",
    ".write": "true"
  }
}

このようになっていますか?
なっていなかったら、上のように変更してください

コードから接続

最後に、ファイル、コードからFirebaseRealtimeDatabaeに接続できるようにします。
行うことはfirebaseの設定が記載されたファイルにDatabaseに関連するコードを追加するだけです。
configで定義しているコードはReactにFirebaseを使ったログイン機能を実装する
を参考に持ってきてください。

firebase/firebase.js
import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'
//追加するコード1つ目

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

export const firebaseApp = firebase.initializeApp(config)
export const firebaseDB = firebase.database()
//追加するコード2つ目

このようなファイルを作成することでfirebaseRealtimeDatabaseを使用できるようになります。

データの追加

続いて、データ、メッセージをRealtimeDatabaseに追加できるようにします。
では、解説していきます。

ChatForm.js

ここではユーザー名及びメッセージを入力するフォームを作成しています。
RealtimeDatabaeと直接の関わりはありません。
ここで入力された値がRealtimeDatabaseに渡されるため、src/App.jsで定義されたイベントと照らし合わせて確認してください。

src/components/ChatForm.js
import React,{Component} from 'react'
import firebase from 'firebase/app'
import {firebaseApp} from '../firebase/firebase'

class ChatForm extends Component {
  render(){
    return(
      <div id='Form'>
        <input name='user_name' onChange={this.props.onTextChange} placeholder='名前'/>
        <textarea name='text' onChange={this.props.onTextChange}  placeholder='メッセージ'/>
        <button onClick={this.props.onButtonClick}>送信</button>
      </div>
    )
  }
}

export default ChatForm

ChatMessage.js

ここでは今までのメッセージが表示されます。
メッセージのデータはsrc/App.jsからpropsを使用して渡されます。
RealtimeDatabaseのデータはここに表示されるとだけ覚えておいてください。

ChatMessage.js
import React,{Component} from 'react'

class ChatMessage  extends Component {
  render(){
    return(
      <div>
        <p>{this.props.message.text}</p>
        <p>by&nbsp;{this.props.message.user_name}</p>
      </div>
    )
  }
}

export default ChatMessage

App.js

このファイルがRealtimeDatabaseと直接やり取りをします。
まずはコードをご覧ください。

src/App.js
import React, { Component } from 'react'
import firebase from 'firebase/app'
import { firebaseApp,firebaseDB } from './firebase/firebase'
import ChatMessage from './components/ChatMessage'
import ChatForm from './components/ChatForm'

const messagesRef = firebaseDB.ref('messages')
//RealtimeDatabaseの`messages`という要素にメッセージを保存する

class App extends Component {
  constructor(props) {
    super(props)
    this.onTextChange = this.onTextChange.bind(this)
    this.onButtonClick = this.onButtonClick.bind(this)
   //このファイルで使用するイベントを明確化している
    this.state = {
      text : "",
      user_name: "",
      messages : []
    }
  }

  componentWillMount() {
      messagesRef.on('child_added', (snapshot) => {
        const m = snapshot.val()
        let msgs = this.state.messages
        msgs.push({
          'text' : m.text,
          'user_name' : m.user_name,
        })
        this.setState({ messages : msgs })
      })
    }
  //下で解説

  onTextChange(e) {
  // ChatForm.jsで入力された値がthis.state.user_nameとthis.state.textに格納される
    if(e.target.name == 'user_name') {
      this.setState({
        "user_name": e.target.value
      });
    } else if (e.target.name == 'text') {
      this.setState({
        "text": e.target.value
      });
    }
  }

  onButtonClick() {
  //ChatForm.jsの送信ボタンが押されたらthis.stateに格納された値をRealtimeDatabaseに送信する
    messagesRef.push({
      "user_name" : this.state.user_name,
      "text" : this.state.text
    })
    this.setState({"text": ""})
  }

  render() {
    return (
      <div>
        <div>
          <h2>メッセージログ</h2>
          {this.state.messages.map((m, i) => {
            return <ChatMessage key={i} message={m} />
          })}
          //それぞれのメッセージ毎にiというキーを割り当てて、RealtimeDatabaseにある全てのメッセージを表示する
        </div>
        <ChatForm onTextChange={this.onTextChange} onButtonClick={this.onButtonClick} />
     //ChatForm.jsにここで定義した2つのイベントを渡す
      </div>
    )
  }
}

export default App;

eventなどReactの基本的な内容がわかっていればそこまで難しくはないでしょう。
注目していただきたいのはcomponentWillMount()です。
ここで定義している内容によって、RealtimeDatabaeに送ったメッセージが即座に表示されるようになっています。

では、コメントアウトで解説していきます。

App.js
  componentWillMount() {
  //このファイルのコードが読み込まれる前に行う
      messagesRef.on('child_added', (snapshot) => {
      //RealtimeDatabseにデータが追加されたら以下の操作を行う
        const m = snapshot.val()
     //追加されたデータをmとして扱う
        let msgs = this.state.messages
        //データが追加される前のthis.state.messegesをmsgsに入れる
        msgs.push({
          'text' : m.text,
          'user_name' : m.user_name,
        })
        //msgsに追加されたデータを追加する
        this.setState({ messages : msgs })
     //最新のデータをこのコンポーネントで扱えるようmsgsをthis.state.messagesとして定義する
      })
    }

やっていることはRealtimeDatabaeに追加されたメッセージをthis.state.messages追加しているだけです。
わかりづらかったらconsole.log()を使って、一つずつのコードでどのようなデータが扱われているのかを確認してみてください。

データの削除

続いて、データを削除するコードを実装していきます。
ここで扱うのは、ChatMessage.jsApp.jsです

App.js

App.jsで変更するのはcomponentWillMount()の中の一箇所だけです。

src/App.js
  componentWillMount() {
      messagesRef.on('child_added', (snapshot) => {
        const m = snapshot.val()
        let msgs = this.state.messages
        console.log({msgs})
        msgs.push({
          'text' : m.text,
          'user_name' : m.user_name,
          'key': snapshot.key
        })
        this.setState({ messages : msgs })
        console.log(this.state.messages)
      })
    }

データを削除する際に必要なのは、削除したいメッセージとthis.state.messagesで保存しているメッセージが一致するようにすることです。
RealtimeDatabaseにデータが追加される際、snapshotによって一意のキーが割当られます。
そのキーをthis.state.messagesに保存することで、キーを呼び出せばそのデータを削除できるようになるのです。
実際に削除するのはChatMessage.jsで行います。

ChatMessage.js

RealtimeDatabseでデータの削除はremove()だけで行なえます。
そのためには削除したいデータを保存している場所、キーを見つけ出すことが必要ですが、snapshotのキーをthis.state.messagesに格納したことでメッセージ毎にデータを削除できるようにしました。

src/componentsChatMessage.js
import React,{Component} from 'react'
import { firebaseDB } from '../firebase/firebase'

const messagesRef = firebaseDB.ref('messages')

class ChatMessage  extends Component {
  constructor(props) {
    super(props)
    this.onRemoveClick = this.onRemoveClick.bind(this)
  }
  onRemoveClick(){
     messagesRef.child(this.props.message.key).remove()
     //メッセージ毎にキーがあるためそれを取得して削除する
     alert('メッセージはページを更新した後に削除されます')
  }
  render(){
    return(
      <div>
        <p>{this.props.message.text}</p>
        <p">by&nbsp;{this.props.message.user_name}</p>
        <button onClick={this.onRemoveClick}>削除</button>
     //削除ボタンの実装
      </div>
    )
  }
}

export default ChatMessage

まとめ

今回はページからRealtimeDatabaseへデータの追加、削除する方法をご紹介しました。
削除機能に関して、データを削除してもスグに表示が変更されないため、それが今後の課題となっています。
自身で発見すれば更新しますが、もし良い方法が見つかった際は、コメントなどで教えていただけると助かります。

参考資料

React & Firebaseで簡単なChatアプリを作ってみた
ウェブでのデータの読み取りと書き込み - Firebase Documentation
ウェブ上でデータリストを操作する - Firebase Documentation
How to add remove data function of Firebase Realtime Database - stackoverflow
How to read props on event on React - stackoverflow

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away