PlayでカスタマイズされたプロジェクトでReact.jsを使う

# 完成形

やりたいことは、下記のような形のプロジェクトでsbt(activator)のrunコマンドだけでScalaとJSXのコンパイルとサーバ起動が出来るようになること。

![project](https://qiita-image-store.s3.amazonaws.com/0/3972/a29c76a0-9f2d-9350-8ff0-823fa858bba3.png)

# 手順

## reactjsプラグイン

まず、sbt上でJSXをコンパイルできるようになるプラグインを使います。
[sbt-reactjs](https://github.com/ddispaltro/sbt-reactjs)

plugins.sbtに下記を追記します。

```scala:plugins.sbt
addSbtPlugin("com.github.ddispaltro" % "sbt-reactjs" % "0.6.8")
```

これでsbt上でJSXのコンパイルが出来るようになります。
ちなみにデフォルトではnode.jsを使ってコンパイルしようとするようですが、入っていなくても[js-engine](https://github.com/typesafehub/js-engine)のTriremeを使用するのでひとまず気にしなくて良さそうです。

## プロジェクト設定

ちょっと大きなプロダクトになるとマルチプロジェクト構成にする場合がほとんどだと思います。
今回はPlayの標準の規約ではなくて、独自のパッケージのルールでソースコードを配置したいと思います。


```scala:build.sbt
organization := "my.company"

name := "PlayReact"

version := "1.0"

lazy val commonSettings = Seq(scalaVersion := "2.11.8")

lazy val web = (project in file("web"))
  .settings(commonSettings: _*)
  .enablePlugins(PlayScala)
  .disablePlugins(PlayLayoutPlugin)
  .settings(
    sourceDirectories in (Compile, TwirlKeys.compileTemplates) := Seq((scalaSource in Compile).value / "my" / "company" / "system"),
    sourceDirectories in (Test, TwirlKeys.compileTemplates) := Seq((scalaSource in Test).value / "my" / "company" / "system"),
    sourceDirectory in Assets := (scalaSource in Compile).value / "my" / "company" / "system" / "assets",
    sourceDirectory in TestAssets := (scalaSource in Test).value / "my" / "company" / "system" / "assets",
    resourceDirectory in Assets := baseDirectory.value / "public",
    ReactJsKeys.harmony := true,
    ReactJsKeys.es6module := true,
    ReactJsKeys.stripTypes := true
  )
  .dependsOn(common)

lazy val common = (project in file("common"))
  .settings(commonSettings: _*)

```

最終的に上のような感じにしました。
`.disablePlugins(PlayLayoutPlugin)`でPlay標準のレイアウトを解除します。(app下にソース置かなきゃいけないやつ)
また、Twirlのテンプレートの配置位置も調整しています。

`.jsx`ファイルはAssets扱いなので`sourceDirectory in Assets`にて配置位置の調整を行います。
PlayではSbtWebを使用しているので、sbt-reactjsを使う際に改めてプラグイン有効化を行う必要はありません。
JSXのコンパイルオプションは`ReactJsKeys`で指定できます。

## ルーティング設定

```scala:routes
GET     /                           my.company.system.controllers.Application.index
GET     /api                        my.company.system.controllers.Application.api

GET     /assets/*file               controllers.Assets.at(path="/public", file)
```
FQCNでクラス名を指定しなければいけないところ以外は特に変わったことはありません。

## コントローラーとJSXとビュー

パッケージ以外はいつも通りのコントローラーです。

```scala:Application.scala
package my.company.system.controllers

import my.company.common.Resource
import play.api.libs.json.Json
import play.api.mvc._

class Application extends Controller {

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

  def api = Action {
    Ok(Json.toJson(new Resource().data)) // Seq("hoge1", "hoge2", "hoge3")
  }

}
```


JSXの配置場所はbuild.sbtで指定した`my.company.system.assets`パッケージ下の`javascript`パッケージです。
コードの中身は何の変哲もないJSXファイルです。

```js:app.jsx
const DataList = React.createClass({
    getInitialState: () => {
        return {data: []};
    },
    componentDidMount: function() {
        const self = this;
        superagent
            .get("/api")
            .end(function(err, res){
                self.setState({data: res.body});
            });
    },
    render: function() {
        const toListItem = x => <li>{x}</li>;
        return (
            <ul>
                {this.state.data.map(toListItem)}
            </ul>
        );
    }
});

ReactDOM.render(
    <DataList />,
    document.getElementById("main")
);
```

テンプレートは以下のようになります。
JSXでコンパイルされた物は`@routes.Assets.at("javascripts/ファイル名.js")`でパスが取得できます。

```html:index.scala.hml
@(title: String)
<!DOCTYPE html>
<html>
    <head>
        <title>@title</title>
        <script src="//fb.me/react-with-addons-0.14.8.min.js"></script>
        <script src="//fb.me/react-dom-0.14.8.min.js"></script>
        <script src="//cdnjs.cloudflare.com/ajax/libs/superagent/1.2.0/superagent.min.js"></script>
    </head>
    <body>
        Hello World!!
        <div id="main"></div>
        <script src="@routes.Assets.at("javascripts/app.js")"></script>
    </body>
</html>

```

# 実行結果
#### `http://localhost:9000/`にアクセスして得られるレスポンス

Hello World!!

- hoge1
- hoge2
- hoge3

# 終わり
javascript側はもっといろいろ考えることがありそうですが、ひとまずsbtだけでScalaとJSXのコンパイルができるようになりました。
実際にやってみるとsbtだけでフロントエンドとバックエンドの両方がコンパイル&実行できてとても楽です。