ScalaJSによるReact

# scalajs-reactを使う
ScalaJSでReactを使う場合、scalajs-reactというFacadeを使うのが一般です。残念なことにscalajsのバージョン1.0系以降には対応していないので0.6系を使います。

# 準備
前のエントリのようにプロジェクトのひな型から作ります。
今回はWorkbenchと[scalajs-bundler](https://scalacenter.github.io/scalajs-bundler/)のプラグインを使います。
WorkbenchはScalajsのアプリ用にローカルサーバを立ち上げて、HTMLを表示してくれます。scalajs-bundlerを使うと、jsDependencyとwebjarsを使って依存関係を解決するというオールドスクールなやり方をしないで済みます。Webjarsって便利だけど、参照がうまくいかなかったりするのでこの方が便利です。

```
sbt new scala/scala-seed.g8
```

```project/plugin.sbt
// Sbt plugins
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.26")
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.14.0")
addSbtPlugin("com.lihaoyi" % "workbench" % "0.4.1")
```

```build.sbt
name := "reactjs-test"

version := "0.1"

scalaVersion := "2.12.6"

lazy val root =  (project in file("."))
  .settings(
    scalaJSUseMainModuleInitializer := true,
    libraryDependencies ++= Seq(
      "org.scala-js" %%% "scalajs-dom" % "0.9.6",
      "com.github.japgolly.scalajs-react" %%% "core" % "1.3.1"
    ),
    npmDependencies in Compile ++= Seq(
      "react" -> "16.5.1",
      "react-dom" -> "16.5.1")
  )
  .enablePlugins(ScalaJSPlugin, WorkbenchPlugin, ScalaJSBundlerPlugin)
```

これで準備は完了です。
公式サイトに倣って、簡単なサンプルを実装します。

# サンプルアプリ（Timer）
簡単な、というより公式のでもサイトのサンプルをそのまま実装しています。一応、内部でStateで状態管理しているのでReactっぽいコンポーネントなので、どのように実装すればよいかはわかるかと思います。


```scalajs/Timer.scala
package scalajs

import org.scalajs.dom
import dom.document
import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.html_<^._

import scala.scalajs.js

object Timer{

  def main(args: Array[String]): Unit = {

    val Timer = ScalaComponent.builder[Unit]("Timer")
      .initialState(State(0))
      .renderBackend[Backend]
      .componentDidMount(_.backend.start)
      .componentWillUnmount(_.backend.clear)
      .build

    Timer().renderIntoDOM(document.body)
  }

  case class Point(x: Int, y: Int){
    def +(p: Point) = Point(x + p.x, y + p.y)
    def /(d: Int) = Point(x / d, y / d)
  }

  case class State(secondsElapsed: Long)

  class Backend($: BackendScope[Unit, State]) {
    var interval: js.UndefOr[js.timers.SetIntervalHandle] =
      js.undefined

    def tick =
      $.modState(s => State(s.secondsElapsed + 1))

    def start = Callback {
      interval = js.timers.setInterval(1000)(tick.runNow())
    }

    def clear = Callback {
      interval foreach js.timers.clearInterval
      interval = js.undefined
    }

    def render(s: State) =
      <.div("Seconds elapsed: ", s.secondsElapsed)
  }
}
```

最初の`Timer`の変数宣言のところで、Reactのコンポーネントを指定しています。最初の値である０をCase ClassのStateとして生成し、`initialeState`メソッドの引数としています。次の`renderBackend`は下に示しているコンポーネントの動きを記述しているクラスを指定しています。同クラスの`render`内に書かれている`.<`はScalajs-reactのDSLになります。
次のチェーンメソッドの`componentDidMount`と`componentWillUnmount`はReactコンポーネントのライフサイクルのフックになっているようですね。最後に`build`して使えるようにしています。

これをHTML内に挿入するのは`renderIntoDOM`メソッドを使います。普通のReactだと`ReactDOM.render()`とかやるところですね。

これを表示するHTMLは以下の感じです。何の変哲もないですが、scalajs-bundlerを使っているので、参照するJSファイルのパスが少し違います。ここは要チェックです。

```index-dev.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript" src="/workbench.js"></script>
<script type="text/javascript" src="./target/scala-2.12/scalajs-bundler/main/reactjs-test-fastopt-bundle.js"></script>
</body>
</html>
```

scalajs-bundler含めてのコンパイルとしては、以下のコマンドで行います。

```
fastOptJS::webpack
```

Workbenchが立ち上がっているので、`http://localhost:12345/index-dev.html`にアクセスすれば、単に時間をカウントするだけのアプリが表示されます。

# 所感
まぁ、できることはできますが、Scalajsに慣れていないこともあってとにかくとっつきにくかったです。DSLも相まって混乱を誘います。ただ、よくよく見ると合理性のあるつくりになっており、ScalaでPlayとの連携も容易なので、少し大きなプロジェクトだといい感じに使えるのだと信じています。


# 参考
- [Scala.jsでもReactを使いたい](https://jyuch.hatenablog.com/entry/2018/08/08/001501)
- [scalajs-react](https://japgolly.github.io/scalajs-react/)