質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

1189閲覧

firebase React でチャット機能を実装したいのですが、その前段階でテキストのレンダリングが上手くいきません。

webnakada

総合スコア7

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2020/06/30 22:54

実現したいこと

Reactとfirebaseでチャット機能を実現したいと考えていますが、その前段で思ったとおりの動作をしてくれないので、ご回答をお願いしたいと思っております。どうかよろしくお願いします。質問に回答して頂くにあたって不明点ありましたら追記、返答させて頂きます。

新しいテキストをレンダリングするために、自身で作成したInputコンポーネントにテキストを入力し、Buttonコンポーネントをクリックすると、その入力したテキストが1回レンダリングされるようにしたいのですが、1回のクリックで2回レンダリングされ入力したテキストが2回分表示されてしまします。

発生している問題

inputタグ(コンポーネント)にテキストを入力し、buttonタグ(コンポーネント)を1クリックすると2回テキストが表示される。

コードの説明

1,Chatコンポーネントがレンダリングされた直後にcomponentDidMountでloadText関数を呼び出し、firebaseに書き込んでいるテキストと投稿者を読み出して表示させています。
2,テキストをinputタグに入力し、ボタンをクリックしてhandleSubmit関数を呼び出し、firebaseに書き込みを行う。
3,firebaseへの書き込みが発生した時点で、loadtext関数内部のonSnapshotが作動する。書き込みがされた最新の状態firebaseを読み込み、それをconstructor内部にsetStateしrender()を呼び出す。

私の認識では3でsetStateを一度しか実行していないので、レンダリングも1回しかされないという認識ですが、2回レンダリングうまくいきません。ちなみに、2回表示されるテキストについてコンソールで確認すると、先に表示された方のserverTimeStampがnullで、後に表示された方に正しく値が入っているという違いがありました。

該当のソースコード

chat.component.jsx

JavaScript

1import React from 'react' 2import './chat.styles.scss' 3import Input from '../../component/input/input.component' 4import Button from '../../component/button/button.component' 5import {Link} from 'react-router-dom' 6import {auth,firestore, serverTimeStamp} from '../../firebase/firebase.utils' 7 8class Chat extends React.Component { 9 constructor(){ 10 super() 11 12 this.state={ 13 text:'', 14 displayName:'', 15 uid:'', 16 message:[] 17 } 18 } 19 20 loadText = () =>{ 21 22 firestore.collection('text').orderBy('serverTimeStamp','desc').onSnapshot(snapshot => { 23 24 snapshot.docChanges().forEach(change => { 25 if (change.type == 'removed'){ 26 27 } else { 28 const {text,displayName,serverTimeStamp} = change.doc.data() 29 const message = this.state.message 30 const obj = {text:text,displayName:displayName,serverTimeStamp:serverTimeStamp,id:change.doc.id} 31 this.setState({message:message.concat(obj)}) 32 } 33 34 }) 35 36 }) 37 38 } 39 40 41 componentDidMount(){ 42 auth.onAuthStateChanged(async (user) =>{ 43 if(!user){ 44 alert('ログインしてください') 45 this.props.history.push('/') 46 } else{ 47 const {uid,displayName} = user 48 this.setState({displayName:displayName,uid:uid}) 49 } 50 }) 51 52 this.loadText() 53 } 54 55 56 signOut=()=>{ 57 auth.signOut() 58 } 59 60 61 handleChange = (e) => { 62 const {value} =e.target 63 this.setState({text:value}) 64 } 65 66 handleSubmit = (e) => { 67 e.preventDefault() 68 69 firestore.collection('text').add({ 70 displayName:this.state.displayName, 71 uid:this.state.uid, 72 text:this.state.text, 73 serverTimeStamp:serverTimeStamp() 74 }) 75 this.setState({text:''}) 76 } 77 78 render(){ 79 const convesation= this.state.message.map((message,index) => { 80 return( 81 <div className='message' key={index} > 82 <div>{message.text}</div> 83 <div className='contributor'>投稿者:{message.displayName}</div> 84 </div> 85 ) 86 }) 87 return( 88 89 <div className='chat'> 90 91 <div className='convesation-area'> 92 {convesation} 93 </div> 94 95 <form onSubmit={this.handleSubmit}> 96 <Input 97 type='text' 98 name='text' 99 handleChange={this.handleChange} 100 value={this.state.text} 101 placeholder='テキストを入力してください' 102 /> 103 <Button 104 chlldren='送信' 105 /> 106 </form> 107 <Link className='signout' to='/' onClick={this.signOut}>サインアウト</Link> 108 </div> 109 ) 110 } 111 112} 113 114export default Chat

補足情報(FW/ツールのバージョンなど)

・環境
OS:Windows10

・FWなどののバージョン
"firebase": "^7.15.5",
"node-sass": "^4.14.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1""
"node.js" v12.16.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

loadText関数の

JavaScript

1const {text,displayName,serverTimeStamp} = change.doc.data()

下に、

JavaScript

1if(serverTimeStamp == null){ 2 return 3 }

を書き加えることで解決しました。

理由としては、firebaseのserverTimeStamp()は、実行された瞬間にnullが入り、しばらくすると値が入る仕組みになっており、nullが返された時(added)にonSnapshotが実行され、nullがなにかしらの値に修正(modified)されたときにdocChangesが実行され、結果的にsetStateが2回連続で実行されることになったため1クリックで同じテキストが2回表示されたものと考えられます。

投稿2020/07/01 12:55

webnakada

総合スコア7

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問