はじめに
Rails5とActionCableとreact-railsを使って簡易チャットアプリを作りました。
それぞれ写経したものを組み合わせた感じです
突っ込みどころ満載だと思いますが、ご容赦下さい
実行環境
- Ruby 2.3.0
- Rails 5.0.0.beta3
- react-rails 1.6.2
- redis 3.2.2
参考記事
準備
- Rubyのバージョンを確認(2.2.1以上になっているか)
アプリケーションの作成
# bundle install は一旦スキップで
rails new rails5-app --skip-bundle
Gemを追加
# react-rails
gem 'react-rails'
# redisのコメントアウトを外す
gem 'redis', '~> 3.0'
bundle install
bundle install --path vendor/bundle
migrate(特にDBを使用する予定はありませんが・・・)
# rails5からコマンドがrakeからrailsに変わっているので注意
bundle exec rails db:craete
bundle exec rails db:migrate
react-railsをインストール
bundle exec rails g react:install
コントローラを作成
# showアクションを持つroomコントローラ
bundle exec rails g controller room show
チャンネルを作成
# speakアクションを持つroomチャンネルを作成
bundle exec rails g channel room speak
コンポーネントを作成
bundle exec rails g react:component CommentBox
サーバ側の実装
チャンネルの実装
app/channels/room_channel.rb
class RoomChannel < ApplicationCable::Channel
# おまじない(調査中です)
def subscribed
stream_from "room_channel"
end
# 省略
# クライアントから来たspeakをハンドリング
def speak(data)
ActionCable.server.broadcast 'room_channel', comment: data['comment']
end
end
ルーティングを設定
config/route.rb
Rails.application.routes.draw do
root 'rooms#show'
# コメントアウトを外す
mount ActionCable.server => '/cable'
end
クライアント側の実装
ActionCableを有効に
app/assets/javascripts/cable.coffee
# コメントアウトを外す
@App ||= {}
App.cable = ActionCable.createConsumer()
Railsのviewに追記
app/views/rooms/show.html.erb
<%= react_component('CommentBox')%>
コンポーネントの実装(長いので分けます)
app/assets/javascripts/components/comment_box.js.jsx
var CommentBox = React.createClass({
getInitialState: function() {
return {comments: []};
},
componentDidMount: function() {
this.setupRoomChat();
},
updateCommentList: function(comment) {
comment.id = new Date().getTime();
this.setState({ comments: this.state.comments.concat([comment]) });
},
setupRoomChat: function() {
App.room = App.cable.subscriptions.create("RoomChannel", {
connected: function() {},
disconnected: function() {},
received: function(data) {
// サーバからメッセージを受信
this.updateCommentList(data.comment);
},
// ここが1番謎です(解明中)
updateCommentList: this.updateCommentList.bind(this),
speak: function(comment) {
return this.perform('speak', {
comment: comment
});
}
});
},
handleCommentSubmit: function(comment) {
// サーバにメッセージを飛ばす
App.room.speak(comment);
},
render: function() {
return (
<div className="commentBox">
<h1>Chat Room</h1>
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
<CommentList comments={this.state.comments} />
</div>
);
}
});
app/assets/javascripts/components/comment_box.js.jsx
// ...省略
var CommentForm = React.createClass({
onClick: function(e) {
e.preventDefault();
var text = ReactDOM.findDOMNode(this.refs.text).value.trim();
if (!text) {
return;
}
this.props.onCommentSubmit({text: text});
ReactDOM.findDOMNode(this.refs.text).value = '';
return;
},
render: function() {
return (
<div className="commentForm">
<div>
<input type="text" placeholder="Say something..." ref="text" />
</div>
<div>
<button type="button" onClick={this.onClick}>Post</button>
</div>
</div>
);
}
});
app/assets/javascripts/components/comment_box.js.jsx
// ...省略
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.comments.map(function (comment) {
return (
<Comment key={comment.id}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
{this.props.children}
</div>
);
}
});
実行
bundle exec rails s
確認
ブラウザからlocalhost:3000
にアクセス
2窓で上手くチャット出来ますでしょうか
最後に
ReactのRの字もわからないのでFluxも何を使っていません。
怪しい箇所が何ヶ所もあるので何かあれば教えて頂けると幸いです
github
一応ここにあります。
https://github.com/TekkaMK/rails5-react-chat