こちらはRiot.js Advent Calendar 2017の5日目の記事になります。業務でriotを使う中でハマった細かすぎて伝わらないtipsです。
コンポーネント更新時の挙動が自分のイメージと違っていて、ハマりました。
検証バージョンはv3.7.4
です。
要素のループ
例えば、下記のような要素のループがあったとして、これは配列datas
の中身を子要素に渡すよく見かけるものです。
<component each={v in datas} data={v}/>
this.datas = [
{ value: 0 },
{ value: 1 }
];
このリストのデータを更新したとします。
this.update({
datas: [
{ value: 2 },
{ value: 3 }
]
});
自分の中では、component
のupdate
だけ走って、再マウントはされないイメージでした。
実際には再マウントされる
codepenにサンプルを作りました。
子要素に渡すデータを更新しています。
https://codepen.io/kwst/pen/qVgedv
処理の順番としては下記のようになります。
- randomizeボタンを押す
-
this.datas
が変更される -
component
のunmount
が呼ばれる -
component
のmount
が呼ばれる
<component-template>
<button onclick={this.randomize}>randomize datas</button>
<component each={data in datas} data={data}/>
this.datas = [
{ value: 0 },
{ value: 1 }
];
this.randomize = () => {
this.update({ datas: [
{ value: Math.random() },
{ value: Math.random() }
]});
}
</component-template>
<component>
<p>component {this.opts.data.value}</p>
this.on('mount', () => { console.log('mount'); });
this.on('unmount', () => { console.log('unmount'); });
</component>
参照が変わらない時(プリミティブな値の変更)では再マウントは走りませんでした。
https://codepen.io/kwst/pen/POVMdm
ちゃんとコードを読んだわけではありませんが、おそらく渡ってくるデータの参照が変われば別のコンポーネントとする仕様なのかもしれません。
自分のイメージでは、下記のように考えていました。子要素のshouldUpdate
で更新すべきかどうかをハンドリングするのがスマートかなと思っていたので。
- randomizeボタンを押す
-
this.datas
が変更される -
component
のshouldUpdate
が呼ばれで、更新すべきかどうかを子要素側でハンドリングする -
shouldUpdate
がtureであればcomponent
を更新する
再マウントされないようにするには
3.7.x
から追加されたkey
属性を追加するだけでOKです。変更したい場合はkey
を変更すれば再マウントされます。リストのデータの一意なidなんかを割り当ててやると良いでしょう。
datas
のindex
値をkey
に割り当ててみました。
<component each={data, i in datas} data={data} key={i}/>
追加したバージョン
https://codepen.io/kwst/pen/EbMVPX
ちなみにReactでは
ちなみにReactでは再マウントは走りませんでした。ただ自分がReactのメンタルモデルを引きずっていただけです。
https://codepen.io/kwst/pen/Zawgxq
Reactでもkey
を変更すれば再マウントを走らせることができます。