🚩

Riot.js v3 tips - refs に同じ値を指定した際の落とし穴とその回避方法

2021/01/20に公開2

はじめに

今回は, Riot.js v3(以降 Riot.js) で, 初学者がよくハマるポイントについて紹介したいと思います.

Riot.js には refs という仕組みがあるんですが, 何も気にせずガンガン使うと危ない挙動になるのでサンプルを交えて事象とその回避方法を紹介したいと思います.

Riot.js における refs について

Riot.js には refs という便利な仕組みがあります.
要素に ref という属性に値を指定することで, script 内で refs を使ってその要素にアクセスできるようになる仕組みです.

<input ref='screen_name' />
console.log(this.refs.screen_name.value); // 入力した値が表示される

refs の落とし穴

一見単純な仕組みなんですが実はやっかいな落とし穴があります.
それは ref の値がバッティングした場合の挙動です.

以下のように ref の名前に同じ値を指定していると

<div ref='item'>hoge</div>
<div ref='item'>foo</div>

script で参照したときに配列になってしまうんです!

console.log(this.refs.item); // Array[HTMLDivElement, HTMLDivElement]

意図して書いてたならまだ良いんですが, 以下のように配列の each で重複した ref を指定していた場合, 配列の個数によって単体だったり配列だったりという現象が起きていしまいます.

<div each='{item in items}' ref='item'>{item}</div>
// this.items = ['hoge'] のとき
console.log(this.refs.item); // HTMLDivElement(単体)
// this.items = ['hoge', 'foo', 'bar'] のとき
console.log(this.refs.item); // Array[HTMLDivElement, HTMLDivElement, HTMLDivElement]

つまり, 型が変わってしまうんです💦

API 等で取ってきた値を each で展開してた場合, API のレスポンスの数によって変数の型が変わってしまうという最悪の現象が起きます.

ウチのメンバーもみんな Riot.js を使ってるんですが1度は必ずこの現象で躓いてしまいます.

サンプルプログラム

上記で挙げた現象を再現させた上で, それを回避しているサンプルになります.

https://runstant.com/phi/projects/8bfc5ea8

コード

arrayA, arrayB をそれぞれ ref を指定した要素として each で展開して, script でそれぞれ参照しています.

app.tag
<app>
  <h1>Riot.js v3 tips - refs に同じ値を指定した際の落とし穴とその回避方法</h1>
  
  <h2>array A</h2>
  <ul>
    <li each='{item in arrayA}' ref='a'>{item}</li>
  </ul>
  
  <h2>array B</h2>
  <ul>
    <li each='{item in arrayB}' ref='b'>{item}</li>
  </ul>
  
  <script>
    this.arrayA = ['hoge', 'foo', 'bar'];
    this.arrayB = ['hoge'];
    
    this.on('mount', () => {
      // 配列
      console.log(this.refs.a);
      // 単体
      console.log(this.refs.b);
      
      // 必ず参照する際に配列化する
      var a = Array.isArray(this.refs.a) ? this.refs.a : [this.refs.a];
      var b = Array.isArray(this.refs.b) ? this.refs.b : [this.refs.b];
      
      // 配列の長さに関係なく使える
      a.forEach(item => console.log(item.innerHTML));
      b.forEach(item => console.log(item.innerHTML));
    });
  </script>
</app>

解説

まず refs で取ってきた要素を console でそれぞれ表示しています.

// 配列
console.log(this.refs.a);
// 単体
console.log(this.refs.b);

片方は配列で, 片方は単体になってしまっているのがわかると思います.
これは arrayB が値を1つしかもたない配列だからです.

こういった可能性を考慮して each している要素に ref を指定した場合は必ず以下のように
配列だったらそのまま, 単体だったら配列にする処理を一回挟みましょう!

// 必ず参照する際に配列化する
var a = Array.isArray(this.refs.a) ? this.refs.a : [this.refs.a];
var b = Array.isArray(this.refs.b) ? this.refs.b : [this.refs.b];

これで配列だということが保証されるので, 以降安心して使えます!

// 配列の長さに関係なく使える
a.forEach(item => console.log(item.innerHTML));
b.forEach(item => console.log(item.innerHTML));

おわりに

私は何かサービスを作る際, Riot.js v3 をよく使うんですが, 色々と荒削りな部分があり初学者がハマってしまうポイントが結構あります!

そういうところもかわいいんですよね♪
Riot.js は, そう思えるぐらい正当な HTML の形を崩さずにコーディングしやすく設計された魅力的なライブラリです.

React や Vue に大きく差をつけられちゃってますが, それでも粛々と使い続けて Riot.js の素晴らしさを伝えていけたらと思っています.

以上, マニアックな Tips でした.

Discussion

cigarcigar

自分も漏れなくハマった人間ですw
自分は ref の値がバッティング すること自体を基本避けていましたが、本件の対応で型制御をして安心して使って見ようと思います

phi@rabeephi@rabee

自分も漏れなくハマった人間ですw

だよねw

配列になってくれる仕様って珍しいからむしろ活用していこう♪