Edited at

Riot v4 で子コンポーネントのメソッドを実行する

この記事は そろそろ Riot v4 への移行をしようじゃないか の続きです。

v3 から v4 への移行を考えている方は先に読んでもらえればと。


Riot v4 では子コンポーネントの関数が実行できなくなっています。

コンポーネント間で依存関係を持たないようにという思想だと理解してはいますが、どうしても必要なケースがあったので実行できる方法を考えてみました。


Riot v3 ではどうやっていた?

refs でコンポーネントを取得すれば、タグメソッド(this.xxx)が簡単に実行できました。

<app>

<child ref="child"></child>
<button type="button" onclick="{ showChild }">Show child</button>

<script>
this.showChild = () => {
this.refs.child.show()
}
</script>
</app>

<child>
<script>
this.show = () => {
// ...
}
</script>
</child>


Riot v4 ではどうするの?

riot-observable を使ってイベントを登録しておき、親から trigger で発動すればうまくいきました。

順を追って説明します。


1. 親コンポーネントで riot-observable を初期化して、子コンポーネントに渡す


app.riot

  <app>

- <child ref="child"></child>
+ <child observable="{ observable }"></child>
<button type="button" onclick="{ showChild }">Show child</button>
<script>
- this.showChild = () => {
- this.refs.child.show()
- }
+ import observable from 'riot-observable'
+ export default {
+ onMounted(props, state) {
+ this.observable = observable(this)
+ },
+ showChild() {
+ this.refs.child.show() // とりあえず v3 記法のまま
+ }
+ }
</script>
</app>


2. 子コンポーネントで、uid を生成する

プロパティに index を追加します。

プロパティはv4からstatic変数になった特性を活かして、 onMount でインクリメントして uid を生成しています。


child.riot

  <child>

<script>
+ let index = 0
+ export default {
+ onMounted(props, state) {
+ this.uid = `child-${index++}`
+ },
+ }
this.show = () => {
// ...
}
</script>
</child>

親コンポーネントからアクセスできるように id 属性に uid をセットします。

idを使いたくない場合は、 data-xxx とかなんでもいいです)


child.riot

- <child>

+ <child id="{ uid }">


3. 親コンポーネントから受け取った observable にイベントを登録する

id + イベント名(この例ではshow)で登録します。


child.riot

  <child id="{ uid }">

<script>
let index = 0
export default {
onMounted(props, state) {
this.uid = `child-${index++}`
+ props.observable.on(`${this.uid}-show`, () => {
+ show()
+ })
},
}
- this.show = () => {
+ function show() {
// ...
}
</script>
</child>


4. 親コンポーネントからイベントを実行する

2.で設定したid属性を使ってuidを取り出してイベントを実行すると、目的のメソッドが実行されます。👏


app.riot

  <app>

<child observable="{ observable }"></child>
<button type="button" onclick="{ showChild }">Show child</button>
<script>
import observable from 'riot-observable'
export default {
onMounted(props, state) {
this.observable = observable(this)
},
showChild() {
- this.refs.child.show()
+ this.observable.trigger(`${this.$('child').id)}-show`
}
}
</script>
</app>


補足:riot.installobservableを渡しちゃってもいい

例では observable を親コンポーネントで生成して属性で渡していましたが、

riot.install を使ってすべてのコンポーネントにobservableを渡してしまってもいいと思います。


index.js

import observable from 'riot-observable'

const obs = observable()
riot.install(function (component) {
component.obs = obs
})