React + Firebase + Material UIを用いて短時間でリアルタイムチャットを開発する手順をまとめました。短時間でのサンプル開発のため、今回はシンプルに投稿ができる要素のみになります。
準備
node.js、npmのインストール
brew install nodebrew
mkdir -p ~/.nodebrew/src
nodebrew install-binary latest
# 最新バージョンを確認(今回はv9.5.0であることを確認)
nodebrew list
# nodejsを有効にする
nodebrew v9.5.0
# コマンドのパスを通す
echo 'export PATH=$PATH:/Users/ユーザ名/.nodebrew/current/bin' >> ~/.bashrc
# bash_profileをtouch
touch .bash_profile
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
source ~/.bashrc
# npm、nodeコマンドがたたけるかを確認
node -v
npm -v
IDEの用意
今回は、Atomを使用する事にしました。
参考)
Atom : https://atom.io/
Webstrom : https://www.jetbrains.com/webstorm/
React
Reactプロジェクト作成
参考)
https://reactjs.org/tutorial/tutorial.html
mkdir {指定ディレクトリ}
cd {指定ディレクトリ}
# React tutorialプロジェクトを作成
npm install -g create-react-app
create-react-app tutorial
テスト実行
# http:://localhost:3000/に環境ができていることを確認
npm start
FireBase
コンソールを開き、プロジェクトを作成
https://console.firebase.google.com/u/0/?hl=ja
※無料版は、プロジェクト数が5つまでと制限されているが、申請すれば上限を無料で増やしてくれるらしい。
Firebaseの設定
作成したプロジェクトを開き、Authenticationページへ
右上にある「ウェブ設定」をクリックし、設定情報をコピーします。
これらの値が今回使用するFirebase設定情報になります。
tutorialプロジェクト内に戻り、src/firebase/config.jsを用意し、上記で取得した設定情報を入力し保存します。
以下の文献もとても参考になりました。
https://qiita.com/kazushikawamura/items/58ea222b3cc289882d79
export const firebaseConfig = {
apiKey: "***************",
authDomain: "***************",
databaseURL: "https://***************",
projectId: "***************",
storageBucket: "***************",
messagingSenderId: "***************"
};
src/firebase/index.jsを用意し、Firebase Databaseを使用するための初期化処理を行う設定を書きます。
import firebase from 'firebase';
import { firebaseConfig } from './config.js';
export const firebaseApp = firebase.initializeApp(firebaseConfig);
export const firebaseDb = firebaseApp.database();
Firebaseへデプロイ
npm install -g firebase-tools
firebase init
firebase login
firebase.jsonを以下のように設定します。
{
"database": {
"rules": "database.rules.json"
},
"hosting": {
"public": "build"
}
}
database.rules.jsonを以下のように設定します。
{
"rules": {
".read": "true",
".write": "true"
}
}
Firebase Databaseへのデプロイを実行します。
npm run build
firebase deploy
Material UI
Material UIのインストール
参考)
https://www.npmjs.com/package/material-ui
npm install material-ui
cssを用意する
Reactプロジェクト内の構造を整理するため、今回はデフォルトで用意されているApp.cssなどをsrc/cssに移動しました。
中身については、Material UIを用いるので特にデフォルトのままで利用します。
Componentを作成
再び参考)
https://qiita.com/kazushikawamura/items/58ea222b3cc289882d79
src/components/ChatBox.jsを作成。
チャットの投稿フォームのUIが実装されています。
Materila UIのパーツを利用するには、MuiThemeProviderをimportし、
タグで囲うことで、その中で各種コンポーネントを使用することが出来ます。
http://www.material-ui.com/#/get-started/usage
Material UIのRaisedButton、TextFieldパーツを用いています。
http://www.material-ui.com/#/components/raised-button
http://www.material-ui.com/#/components/text-field
import React from "react";
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';
export default class ChatBox extends React.Component {
render() {
return (
<MuiThemeProvider>
<div className="ChatBox">
<div className="">
<TextField name='user_name' onChange={this.props.onTextChange} className="" placeholder="Name" />
<br />
<TextField name='profile_image' onChange={this.props.onTextChange} className="" placeholder="Profile Image URL" />
</div>
<TextField rows="4" multiLine="true" name='text' className="" onChange={this.props.onTextChange} />
<RaisedButton primary="true" label="Send" className="" onClick={this.props.onButtonClick} />
</div>
</MuiThemeProvider>
);
}
}
src/components/Message.jsを作成。
投稿されたチャット内容のUIを実装します。
Material UIのAvatar、List、ListItem、Chipパーツを用いています。
http://www.material-ui.com/#/components/avatar
http://www.material-ui.com/#/components/list
http://www.material-ui.com/#/components/chip
import React from "react";
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import Avatar from 'material-ui/Avatar';
import List from 'material-ui/List/List';
import ListItem from 'material-ui/List/ListItem';
import Chip from 'material-ui/Chip';
const styles = {
chip: {
margin: 4,
},
wrapper: {
display: 'flex',
flexWrap: 'wrap',
},
};
const style = {margintop: -5};
export default class Message extends React.Component {
render() {
return (
<div className="Message">
<List>
<ListItem disabled="true">
<Avatar className="" src={this.props.message.profile_image} />
<span style={{marginBottom: -5}}>@{this.props.message.user_name}</span>
<div className="">
<Chip style={styles.chip} >
{this.props.message.text}
</Chip>
</div>
</ListItem>
</List>
</div>
);
}
}
src/components/AppChat.jsを作成。
チャット全体のUIを構成します。
上記で用意したChatBox、Messageを用います。
また、onTextChange()、onButtonClick()を用意して、各種アクションを実装します。
import React, { Component } from 'react';
import './../css/App.css';
import { firebaseDb } from './../firebase/index.js'
import Message from './Message.js'
import ChatBox from './ChatBox.js'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
const messagesRef = firebaseDb.ref('messages')
class AppChat extends Component {
constructor(props) {
super(props);
this.onTextChange = this.onTextChange.bind(this)
this.onButtonClick = this.onButtonClick.bind(this)
this.state = {
text : "",
user_name: "",
profile_image: "",
messages : []
}
}
render() {
return (
<MuiThemeProvider>
<div className="App">
<div className="App-header">
<h2>Chat</h2>
</div>
<div className="MessageList">
{this.state.messages.map((m, i) => {
return <Message key={i} message={m} />
})}
</div>
<ChatBox onTextChange={this.onTextChange} onButtonClick={this.onButtonClick} />
</div>
</MuiThemeProvider>
);
}
onTextChange(e) {
if(e.target.name == 'user_name') {
this.setState({
"user_name": e.target.value,
});
} else if (e.target.name == 'profile_image') {
this.setState({
"profile_image": e.target.value,
});
} else if (e.target.name == 'text') {
this.setState({
"text": e.target.value,
});
}
}
onButtonClick() {
if(this.state.user_name == "") {
alert('user_name empty')
return
} else if(this.state.text == "") {
alert('text empty')
return
}
messagesRef.push({
"user_name" : this.state.user_name,
"profile_image" : this.state.profile_image,
"text" : this.state.text,
})
}
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,
'profile_image' : m.profile_image,
})
this.setState({
messages : msgs
});
})
}
}
export default AppChat;
src/index.jsを編集し、上記AppChat.jsを呼び出すようにする
import React from 'react';
import ReactDOM from 'react-dom';
import './css/index.css';
import registerServiceWorker from './registerServiceWorker';
import AppChat from './components/AppChat';
import injectTapEventPlugin from 'react-tap-event-plugin';
ReactDOM.render(<AppChat />, document.getElementById('root'));
registerServiceWorker();
## チャットアプリを実行
npm start
感想・まとめ
- Reactを今回はじめて触れていきましたが、これまでのHTML、jsの書き方とは若干異なるためコーディングを覚えていく必要があると感じた。
- 今回は特に触れていないが、Reduxを用いるものになってくるとより複雑な状態遷移のアプリケーションも開発することができるとのことなので、今後触れていきたい。
- Firebaseは、思惑通りリアルタイムチャットを用意に実現してくれたので本件については非常に満足したが、細かい用途に対してどこまでカスタマイズしていけるかは引き続き要調査。
- Material UIは、bootstrapのようなシンプルでスッキリしたUIを簡単に用意することができる。また、カスタマイズに関しても柔軟なようなので今後もReactプロジェクトでは活用していきたい。
- 今回のチャットサンプルの実装でも、他Qiita文献には大変お世話になりました。感謝。
- 実際1時間で作れたかと言うと、間に別件対応なども入ってしまったため、実際には3時間程度かかってしまいました。それらがなくストレートにいければ、この手順を見ながら1時間以内で作っていけるかと思います。