この記事は そろそろ 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>
- <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>
<script>
+ let index = 0
+ export default {
+ onMounted(props, state) {
+ this.uid = `child-${index++}`
+ },
+ }
this.show = () => {
// ...
}
</script>
</child>
親コンポーネントからアクセスできるように id
属性に uid
をセットします。
(id
を使いたくない場合は、 data-xxx
とかなんでもいいです)
- <child>
+ <child id="{ uid }">
3. 親コンポーネントから受け取った observable
にイベントを登録する
id
+ イベント名(この例ではshow)で登録します。
<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>
<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.install
でobservable
を渡しちゃってもいい
例では observable
を親コンポーネントで生成して属性で渡していましたが、
riot.install
を使ってすべてのコンポーネントにobservable
を渡してしまってもいいと思います。
import observable from 'riot-observable'
const obs = observable()
riot.install(function (component) {
component.obs = obs
})