🔗 コントローラ間通信にコールバックを使う(翻訳)
- 単一のStimulusJSコンポーネントで複数のロジックを管理したい場合
- 機能を実行するために他のコントローラの値が必要な場合
- あらゆるケースに対応するためにイベントをたくさん作りたくない場合
話を簡単にするために、以下の例ではカスタムイベントのコンストラクタが不要なjQueryを使っていますが、素のJavaScriptも使えます。
Bad
// first_controller.js
export default class extends Controller {
setName(value) {
this.name = value
$(document).trigger('first_controller.nameChanged', this.name)
}
}
// second_controller.js
export default class extends Controller {
connect() {
// このコントローラでは、UIを何らかのレンダリングするためのnameが必要
$(document).on('first_controller.nameChanged', function(event, name) {
// このnameで何らかの処理を行うコードをここに書く
this.name = name
}.bind(this))
}
}
Good
// first_controller.js
export default class extends Controller {
connect() {
$(document).on('first_controller.state', function(event, callback) {
callback(this)
}.bind(this)
)
}
setName(value) {
this.name = value
}
}
// second_controller.js
export default class extends Controller {
render() {
$(document).trigger('first_controller.state', function(firstController) {
// first_controller のstate/dataを取得するコードをここに書く
this.name = firstController.name
// second_controller固有のUIをレンダリングするコードをここに書く
})
}
}
そうする理由
コードが成長すると、多くのコントローラが他のコントローラのデータを必要とするようになるかもしれません。イベントトリガーをいくつも書いてコントローラ同士を連携することも一応可能ですが、冗長かつ非効率です。
データを持つコントローラは、必ずしもあらゆるインスタンスでイベントをトリガーする必要が生じるとは限りません。代わりに上の方法を使えば、各コントローラがコールバックで自分自身を返すようにできます。
注意
コールバックの目的を設計で明確にしておくべきです。たとえば、この手法を乱用して別のコントローラがアクションをトリガーしまくるようなことは避ける必要があります。そのようなことをすると、「コントローラは単一のコンポーネントを扱うべき」というStimulusJSの設計が損なわれてしまいます。
しかし、何らかの形でコントローラ同士の通信を行うことが避けられない場合は、この方法でコードを整理して共通のインターフェイス(message busデザインパターンなど)を提供しましょう。
参考資料
https://rails.substack.com/p/stimulusjs-finding-out-the-state
概要
原著者の許諾を得て翻訳・公開いたします。