本記事では、ReactNative+Node.js+Pusher+react-native-gifted-chatの構成でリアルタイムチャットを構築する手順をチュートリアル形式で解説します。ある程度本格的なアプリを企画するとチャットでユーザ同士のコミュニケーションを促したくなるはず。
全手順を説明&ソースコードがGithub上にあるのでReactNativeやNode.jsの経験がそこまでない人でも読めるようになっています!!
作るもの

ソースコード
Pusherアプリケーションを作る
まず最初にPusherアカウントを作ります。Pusherはサーバとクライアントをリアルタイム通信してくれるサービスです。無料プランがあるのでちょっとしたコードを書くときにも気軽に使えます。
App ID、App Key、App SecretとClusterを取得します。
アカウントを持っていない方は作成しましょう!
アカウントを作成したらCreate New Appを選択します。
適当にアプリケーションの名前を決めて Create my appを押します。
ここにApp ID、App Key、App SecretとClusterが書いてあります。
以上でPusherの設定は終わりです。
サーバサイドを作る
Expressフレームワークを使ってNode.js上で動くサーバを実装します3つのRESTfulエンドポイントから構成されます。
[PUT]/users/ – ユーザがチャットルームに参加する
[DELETE]/users/ – ユーザがチャットルームから退出する
[POST]/messages – チャットルームにメッセージを投稿する
それでは、Node.jsアプリケーションを作って行きます。
mkdir chat-server
cd chat-server
npm init
色々聞かれますが、すべてEnterを押しましょう。
次に必要なパッケージをインストールします。
npm install --save express body-parser pusher
index.jsを以下のようにしましょう。
const express = require('express');
const bodyParser = require('body-parser');
const Pusher = require('pusher');
const pusherConfig = require('./pusher.json');
const pusherClient = new Pusher(pusherConfig);
const app = express();
app.use(bodyParser.json());
app.put('/users', function(req, res) {
console.log('ユーザが参加しました:' + req.params.name);
pusherClient.trigger('chat_channel', 'join', {
name: req.body.name
});
res.sendStatus(204);
});
app.delete('/users', function(req, res) {
console.log('ユーザが退出しました:' + req.params.name);
pusherClient.trigger('chat_channel', 'part', {
name: req.body.name
});
res.sendStatus(204);
});
app.post('/messages', function(req, res){
console.log('ユーザ:' + req.body.name + 'メッセージ: ' + req.body.message);
pusherClient.trigger('chat_channel', 'message', {
name: req.body.name,
message: req.body.message
});
res.sendStatus(204);
});
app.listen(4000, function() {
console.log('App listening on port 4000');
});
ここでは先ほど紹介した3つのエンドポイントを定義しています。
[PUT]/users/ – ユーザがチャットルームに参加する
[DELETE]/users/ – ユーザがチャットルームから退出する
[POST]/messages – チャットルームにメッセージを投稿する
それぞれのエンドポイントが叩かれたときPusherClientがイベントをトリガーしてそれを購読するフロントエンド側にプッシュ通知が行くようになります。
またpusher.jsonを作って以下のようにしてください。
{
"appId": "XXXXXX",
"key": "XXXXXX",
"secret": "XXXXXXX",
"cluster": "XXX",
"encrypted": true
}
ここには先ほどPusherアプリケーションを作成して得られた値を入れてください。
完了したら以下のコマンドでサーバを起動してみてください。
sudo node index.js
うまくいけばlocalhost:4000でサーバが起動します。コンソールにApp listening on port 4000と表示されるはずです。
例えばこんな感じでリクエストを送ると下の画像のようにPusherのイベントが発行されます。
以上でサーバサイドの実装は終わりです。
フロントエンドを作る
それではReact Nativeを使ってフロントエンドを作って行きましょう。creact-react-native-appを使って作ります。詳しい設定方法はReact Native入門〜Expoを使ってお手軽開発にまとめたので設定がお済みでない方はぜひ一度読んでみてください!
create-react-native-app chat-client
cd chat-client
npm start
と表示されるので好きなオプションを選んでください。iを押すとiOSシュミレータが起動してアプリが起動します。
ログイン画面の作成
まず名前を入力してチャットルームに入室できる画面を作ります。
import React from 'react';
import { StyleSheet, Text, TextInput, KeyboardAvoidingView } from 'react-native';
export default class Login extends React.Component {
render() {
return(
<KeyboardAvoidingView style={styles.container} behavior="padding">
<Text>名前を入力してください!</Text>
<TextInput
autoCapitalize="none"
autoFocus
keyboardType="default"
maxLength={20}
palaceholder="Username"
returnKeyType="done"
enablesReturnKeyAutomatically
style={styles.username}
onSubmitEditing={this.props.onSubmitName}
/>
</KeyboardAvoidingView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
username: {
alignSelf: 'stretch',
textAlign: 'center'
}
})
チャット画面の作成
まずは必要なライブラリをインストールします。UIの画面を作るためにreact-native-gifted-chatというライブラリを使います。またPusher上で発行されたイベントを購読するためにpusher-jsというライブラリもインストールします。shortidはユニークな値を生成するために入れておきます。
npm install react-native-gifted-chat pusher-js shortid --save
ChatClient.jsを作成して以下のようにします。
import React from 'react';
import Pusher from 'pusher-js/react-native';
import { StyleSheet, Text, KeyboardAvoidingView } from 'react-native';
import pusherConfig from './pusher';
import { GiftedChat } from 'react-native-gifted-chat';
const shortid = require('shortid');
export default class ChatClient extends React.Component {
constructor(props) {
super(props);
this.state = {
messages: [],
};
this.pusher = new Pusher(pusherConfig.key, pusherConfig);
this.chatChannel = this.pusher.subscribe('chat_channel');
this.chatChannel.bind('pusher:subscription_succeeded', () => {
this.chatChannel.bind('join', (data) => {
this.handleJoin(data.name);
});
this.chatChannel.bind('part', (data) => {
this.handlePart(data.name);
});
this.chatChannel.bind('message', (data) => {
this.handleMessage(data.name, data.message);
});
});
this.handleSendMessage = this.onSendMessage.bind(this);
this.onSendMessage = this.onSendMessage.bind(this);
}
handleJoin(name) {
if(name == this.props.name) {return;}
const id = shortid.generate();
this.setState({
messages: GiftedChat.append(
this.state.messages,
{
_id: id,
user: {name:name},
text: 'ただいま参加いたしました!'
}
)
});
}
handleMessage(name, message) {
if(name == this.props.name) {return;}
const id = shortid.generate();
this.setState({
messages: GiftedChat.append(
this.state.messages,
{
_id: id,
user: {name:name},
text: message
}
)
});
}
componentDidMount() {
const payload = {
name : this.props.name
};
fetch(`${pusherConfig.restServer}/users`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
}
componentWillUnmount() {
const payload = {
name: this.props.name
};
fetch(`${pusherConfig.restServer}/users`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
}
onSendMessage = (text) => {
const payload = {
name: this.props.name,
message: text
};
fetch(`${pusherConfig.restServer}/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
}
onSend(messages) {
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, messages),
}));
for(let message of messages) {
this.onSendMessage(message.text);
}
}
render() {
const messages = this.state.messages;
return (
<GiftedChat
messages={this.state.messages}
onSend={messages => this.onSend(messages)}
user={{
_id: this.props.id,
name: this.props.name,
}}
/>
)
}
}
componentDidMountでPusherイベントの購読を開始し、Pusherで発行されるjoin、part、messageに対してコールバックを指定しているのがポイントです。
最後にApp.jsを以下のようにします。
import React from 'react';
import Login from './Login';
import ChatClient from './ChatClient';
const shortid = require('shortid');
export default class App extends React.Component {
constructor(props) {
super(props);
this.handleSubmitName = this.onSubmitName.bind(this);
this.state = {
hasName: false
}
}
onSubmitName(e) {
const name = e.nativeEvent.text;
this.setState({
id: shortid.generate(),
name,
hasName: true
});
}
render() {
if(this.state.hasName) {
return (
<ChatClient id={this.state.id} name={this.state.name} />
);
} else {
return (
<Login onSubmitName={this.handleSubmitName}/>
);
}
}
}
ログイン状態を判定し、コンポーネントを出し分けてる処理を実装しています。
ここまでできたら先ほどのNodeサーバを起動し、
npm start
してみてください。
Pusher上でイベントを発行してみたり、エンドポイントを叩いてみたりすると画像のようにリアルタイムに更新されるチャットができると思います。

最後までお付き合いいただきありがとうございました〜〜

WINDII

最新記事 by WINDII (全て見る)
- Canvaが最高すぎる。使い方を完全ガイド【チュートリアルあり】 - 2019年5月14日
- 人気急上昇中のLaravelをはじめよう!【徹底解説】 - 2019年4月23日
- Laravelの認可を理解して実装してみよう! - 2019年3月29日
この記事へのコメントはありません。