Riot Route でルーティング時のレンダリング前に何らかの処理をさせる手っ取り早い方法

 本記事は [Riot.js Advent Calendar 2019](https://qiita.com/advent-calendar/2019/riotjs) の第22日目です.

以下, 注意事項です.

* [Riot.js](https://riot.js.org/) を `riot` と呼ぶ
* [@riotjs/route](https://github.com/riot/route) を `route` と呼ぶ
* 本記事の route は `v5.2.0(執筆時点の最新)` とする
※v5 については別途記事を書き中 :bow: 

# `onBeforeRoute` イベントハンドラの生成

route のソースコードを見ていくと, 2つ riot コンポーネントが見つかりますが, その中の [route-hoc.riot](https://github.com/riot/route/blob/master/src/components/router-hoc.riot) を見てみますと,

```js:router-hoc.riotの抜粋
onBeforeRoute(path, pathToRegexp) {
  if (!match(path, this.state.pathToRegexp)) {
    this.update({
      route: null
    })
  }
},
```

何やら嬉しい名前のメソッドが定義されていますw また, [index.js](https://github.com/riot/route/blob/master/src/index.js) の中を見てみますと, 

```js:index.js
// 省略
import Route from './components/route-hoc.riot'
import Router from './components/router-hoc.riot'
// 省略
export {
  // 〜
  Router,
  Route
}
```

ふむふむ🤔よくわからないが, 使われているのは確かですね. では上手いことこの `onBeforeRoute` を使ってみましょう.

# 差し込み処理の追加

今回は公式のデモの一つ [standalone-history.html](https://github.com/riot/route/blob/master/demos/standalone-history.html) をお借りします. `<router>, <route>` などのタグベースではなく, JS ベースでのルーティングの実装ですね.

```html:standalone-history.html(変更前)
<nav>
  <a href="/hello">Hello</a>
  <a href="/user">User</a>
  <a href="/user/gianluca">Username</a>
  <a href="/goodbye">goodbye</a>
</nav>
<div id="root"></div>
<script type="module">
  const { initDomListeners, setBase } = route

  const loc = window.location

  setBase(`${loc.protocol}//${loc.host}`)

  const onRoute = (url) => root.innerHTML = `${url} and params=${JSON.stringify(url.params)}`

  route.route('/hello').on.value(onRoute)
  route.route('/user').on.value(onRoute)
  route.route('/user/:username').on.value(onRoute)
  route.route('/goodbye').on.value(onRoute)

  initDomListeners()
</script>
```
`nav` にリンクが並んでいるだけの簡単な SPA で, クリックすると対応したテキストが下の div タグに表示されます.

ではこちらに `onBeforeRoute` メソッドを追加していきます.

```diff:standalone-history.html(変更後)

  <nav>
    <a href="/hello">Hello</a>
    <a href="/user">User</a>
    <a href="/user/gianluca">Username</a>
    <a href="/goodbye">goodbye</a>
  </nav>
  <div id="root"></div>
  <script type="module">
    const { initDomListeners, setBase } = route

    const loc = window.location

    setBase(`${loc.protocol}//${loc.host}`)

    const onRoute = (url) => root.innerHTML = `${url} and params=${JSON.stringify(url.params)}`
+   const onBeforeRoute = () => {
+     // 現在のルーティングパスを取得し表示
+     alert(route.getCurrentRoute())
+   }

+   route.route('/hello').on.value(onBeforeRoute)
    route.route('/hello').on.value(onRoute)
    route.route('/user').on.value(onRoute)
    route.route('/user/:username').on.value(onRoute)
    route.route('/goodbye').on.value(onRoute)

    initDomListeners()
  </script>
```

では実行してみましょう.

<img width="882" alt="127_0_0_1_8080_の内容_と_Standalone_History_Demo.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48034/d9f6f2fc-8755-d9c0-a2e9-1bf19274b0cf.png">

先に `Username` リンクをクリックしており, div タグには `/user/gianluca` のテキストが表示されています. ここで `Hello` リンクをクリックしたものが↑のキャプチャになります.

 alert で表示されるテキストは `/hello` となっており, `Hello` のルーティングそのものは実行されていますが, まだテキストは書き換わっていないため, レンダリングはブロックされています.

# おわりに

あくまでこれは簡易的な方法で, 実際にはこれではダメだと思っています…というのも, 先程の処理を以下のように書き換えると, 先に `onRoute!!` のアラートが表示されます.

```diff:抜粋
-    const onRoute = (url) => root.innerHTML = `${url} and params=${JSON.stringify(url.params)}`
+    const onRoute = (url) => {
+      root.innerHTML = `${url} and params=${JSON.stringify(url.params)}`
+      alert("onRoute!!")
+    }
   const onBeforeRoute = () => {
     // 現在のルーティングパスを取得し表示
     alert(route.getCurrentRoute())
   }

-   route.route('/hello').on.value(onBeforeRoute)
    route.route('/hello').on.value(onRoute)
+   route.route('/hello').on.value(onBeforeRoute)
```

これはちょっと細かい話をすると, route は内部的には [rawth](https://github.com/GianlucaGuarini/rawth) というライブラリを使っており[^1] , さらに rawth は内部的には [erre](https://github.com/gianlucaguarini/erre) というライブラリに依存しています.

[^1]: 厳密には route のコア機能はほぼ全てこの子の機能

で, この子は `.on` で検知するルーティングを配列で管理しており, 順番に実行されるため, 先に `onRoute!!` が表示される形となります. ですので, `route.route('/hello')` で__セットする順番を先に書く必要があります__.

本当は正しい使い方があると思いますが, 一旦これで動いているのでまた何か分かったら更新したいと思います.

ではでは(=ﾟωﾟ)ﾉ

### 参考

* [Riot.js v4対応 Router](https://qiita.com/nekijak/items/bb87e1eec71f4b95190c)
* [Riot.js v4対応 Router その２](https://qiita.com/nekijak/items/769f0e349f977a2cbebe)
* [Riot.js v4 自分でRouterを作る](https://qiita.com/nekijak/items/52b5bb11651cc5334624)