don-bra.co

さいきんはphpとたわむれてます

ActionCableでreceivedが複数回呼ばれるとき

まとめ

Subscriptionをつなぐjsファイルを複数回呼び出しているのが原因でした。
<channel name>.js (もしくは.coffee) というファイルです。

App.room = App.cable.subscriptions.create({
}, {
  connected: function() {
    // Called when the subscription is ready for use on the server
  },

  disconnected: function() {
    // Called when the subscription has been terminated by the server
  },

  received: function(data) {
    // Called when there's incoming data on the websocket for this     
  }
});

これです。

原因

なぜ複数回呼び出していたのかというと、Sprockets(とActionCableも)の動作を正しく理解していないためでした。
Sprocketsについては別記事で書くのですが、簡単に今回の原因を説明すると、application.jsのrequire_treeを消したつもりで消していなかったためです。

上で書いたようにroom.jsを読み込むと1subscription作成されます。

application.jsrequire_tree する → channels/room.jscable.js を読み込み

cable.jsrequire_tree ./channels する → channels/room を読み込み

本来はSprocketsがよしなにしてくれて room.js は一度しか読み込まれません。
しかし今回はerb側でapplication.jsとcable.jsをそれぞれ javascript_include_tag で読み込んでいたために、ActionCableのreceivedが2回呼ばれてしまっていました。

おわりに

Webpackの勉強をしないとなあとか、Sprocketsから乗り換えようかなあと思っていたときなのでSprocketsの勉強ができるいい機会でした。
RailsでJS辛い問題はまだJSをゴリゴリ書くようなアプリを作れていないのでわからないのですが今後どこかで遭遇する、しないといけないなあと思っています。