- Ruby on Rails 5から導入されたリアルタイム通信機能であるActionCable
- 浮き沈みの激しいフロントエンドフレームワーク界隈で強い人気を誇るReact
- 一定規模以上のReact開発では欠かせないアーキテクチャであるRedux
今回はこの3つの技術を使用してチャットツールを開発したときの経験を元に、React + Redux でActionCableに通信を行うための書き方についてざっくり紹介します。
各々の環境については構築済であることを前提に進めていきます。
Railsの実装
最低限のリアルタイム通信を実装するならば、Rails側のActionCableはマニュアル通りの実装で問題ありません。
フロント側の記述の例を分かりやすくする為にチャンネルのサンプルを置いておきます。
フロントからデータを受け取って、同じチャンネルにいる接続に向けてブロードキャストするというシンプルな構成です。
class SampleChannel < ApplicationCable::Channel def subscribed stream_from "sample" end def unsubscribed end def speak(data) ActionCable.server.broadcast "sample", comment: data['message'] end end
ちなみにクライアント側の接続処理はRailsがデフォルトで生成する次のJavaScriptコードがよしなにやってくれています。
// Action Cable provides the framework to deal with WebSockets in Rails. // You can generate new channels where WebSocket features live using the `rails generate channel` command. // //= require action_cable //= require_self //= require_tree ./channels (function() { this.App || (this.App = {}); App.cable = ActionCable.createConsumer(); }).call(this);
React&Reduxの実装
フロント側の実装も単純な機能であればシンプルな記述で実現できます。
メインのクラス配下に以下のメソッド記述します。
Action
ActionCableから通信を受けたときのアクションを実装します。
const RECEIVED = 'RECEIVED'; function receivedMessage(messages) { return { type: RECEIVED, messages, }; }
Reducer
Storeの状態を渡されたActionに応じて変更します。
ここでは現在画面に表示しているmessagesステータスに対して、ActionCableよりブロードキャストされたデータを追加しています。
const initialState = { messages: [], }; function sample(state = initialState, action) { switch (action.type) { case 'RECEIVED': return Object.assign({}, state, { messages: state.messages.concat(action.messages), }); default: return state; } }
メイン処理
ActionCableと通信を行うためのメインの処理となります。
それぞれ接続時、切断時、受信時、送信時の処理を書いています。
class SampleMain extends React.Component { // ActionCable componentDidMount() { // ビューのレンダリングが終わったらActionCableに接続 this.subscriptChannel(); } subscriptChannel() { App.sample = App.cable.subscriptions.create( // ActionCableで実装したチャンネル名、paramに設定したい値 { channel: "SampleChannel", sample_id: "sample_id" }, { 接続されたときの処理 connected() { }, // 切断されたときの処理 disconnected() { }, // ActionCableからの通信を受けたときの処理 received(data) { // 受信したデータで更新する store.dispatch(Actions.receivedMessage(data['comment'])) }, speak(message) { // サーバー側のメソッドの呼び出し this.perform('speak', {message: message}); } } ); App.sample.received = App.sample.received.bind(this); } // 以下省略 }
あとはフォームボタンクリック時にサーバー側のメソッドの呼び出しを実装すればOKです。
class FormInput extends React.Component { send(e) { e.preventDefault(); //上で書いたサーバー側の呼び出し App.sample.speak(this.myInput.value); this.myInput.value = ''; return; } render() { return ( <form> <input type="text" ref={(ref) => (this.myInput = ref)} defaultValue="" /> <button onClick={(event) => this.send(event)}>Send</button> </form> ); } }
まとめ
React/Reduxは実装方法が柔軟なため、サンプル毎に実装が異なっていて情報を見つけるのに結構苦労しました。
今回の記事がお役に立てば幸いです。