create-react-kotlin-appでイケてるフロントエンド開発

## Kotlinでフロントエンドを書きたい！
Kotlinには、Kotlin/JSという **KotlinをJavaScriptにトランスパイル出来る機能** があります。
これによりKotlinのモダンな文法でフロントエンドやサーバーサイドJSの開発を行うことができます。

フロントエンドでは、公式から [React用のKotlinラッパー](https://www.npmjs.com/package/@jetbrains/kotlin-react) が提供されており、Kotlinを使ったReactの開発が出来るようになっています。
また、create-react-appのように、[create-react-kotlin-app](https://github.com/JetBrains/create-react-kotlin-app) というものも用意されており、これを使えば面倒な設定無しですぐに開発ができます。

今回は、create-react-kotlin-appを使って [Reactの公式チュートリアル](https://ja.reactjs.org/tutorial/tutorial.html#lifting-state-up-again) をやってみたので、それを見ながらKotlinを使ったReact開発についてお話したいと思います。

環境構築などは公式のREADMEを参照してください。

コードだけ見たい方は [こちら](https://github.com/Umisyo/my-react-kotlin-app)

### 追記
READMEの日本語訳が公式にマージされました！
英語が辛くて進められない方はぜひ[こちら](https://github.com/JetBrains/create-react-kotlin-app/blob/master/README-ja.md)をご参照ください！

## この記事が対象とする人
+ JavaScriptはちょっと分かる人
+ Reactのチュートリアルくらいはやったことがあるレベル以上の人
+ Kotlinに興味がある人
+ Kotlinがちょっとわかる人

## ディレクトリ構造
```sh
src
├── Board
│   ├── Board.css
│   └── Board.kt
├── Game
│   ├── Game.css
│   └── Game.kt
├── Square
│   ├── Square.css
│   └── Square.kt
└── main
    ├── main.css
    └── main.kt
```

## 関数コンポーネント
関数コンポーネントは、例えばチュートリアルのSquareコンポーネントは次のように書くことができます。

```Square.kt
package Square

import kotlinx.html.js.onClickFunction
import org.w3c.dom.events.Event
import react.RBuilder
import react.dom.button

fun RBuilder.square(value: String?, onClickFunction: (Event) -> Unit) =
        button(classes = "square"){
            +(value ?: "")
            attrs.onClickFunction = onClickFunction
        }
```

Kotlinでrenderを書くときは、RBuilderというクラスの **拡張関数** として定義します。
今回の場合は `square` という拡張関数を定義しました。
これはpropsを引数として受け取る事ができ、その **型を指定することもできます**。
Kotlinは **null安全** な言語なので、`String?` のようにオプショナルな型(nullを許容する型)を指定することもできます。（そうしない場合、nullが入ろうとするとビルドの段階で弾いてくれます）
また、Elmのように、HTMLタグは関数として書くことができます。

## コンポーネント
通常のコンポーネントは、以下のように記述します。

```Board.kt
package Board

import Square.square
import react.RBuilder
import react.RComponent
import react.RProps
import react.RState
import react.dom.div

class Board: RComponent<Board.Props, RState>() {
    interface Props: RProps {
        var squares: Array<String?>
        var onClickFunction: (Int) -> Unit
    }

    private fun RBuilder.renderSquare(i: Int) {
        square(value = props.squares[i],
                onClickFunction = { props.onClickFunction(i) })
    }

    override fun RBuilder.render() {
        div {
            div(classes = "board-row") {
                renderSquare(0)
                renderSquare(1)
                renderSquare(2)
            }
            div(classes = "board-row") {
                renderSquare(3)
                renderSquare(4)
                renderSquare(5)
            }
            div(classes = "board-row") {
                renderSquare(6)
                renderSquare(7)
                renderSquare(8)
            }
        }
    }
}

fun RBuilder.board(squares:Array<String?>, onClickFunction: (Int) -> Unit) = child(Board::class) {
    attrs.squares = squares
    attrs.onClickFunction = onClickFunction
}
```

まず、RComponentクラスを継承したクラスとして、コンポーネントのクラスを定義します。
Propsを受け取る場合はこのように、 RPopsを継承する `interface` として定義します。
当然こちらもしっかりと型を記述することができます、見やすくていいですね。

```kotlin
    interface Props: RProps {
        var squares: Array<String?>
        var onClickFunction: (Int) -> Unit
    }
```

## ステート
```Game.kt
package Game

import Board.board
import kotlinx.html.js.onClickFunction
import react.RBuilder
import react.RComponent
import react.RProps
import react.RState
import react.dom.button
import react.dom.div
import react.dom.li
import react.dom.ol
import kotlin.arrayOf
import react.setState

class Game: RComponent<RProps, Game.State>() {
    init {
       state.apply {
           history = arrayOf(HistoryEntry(Array(9) { null }))
           xIsNext = true
           stepNumber = 0
       }
    }

    interface State: RState {
        var history: Array<HistoryEntry>
        var xIsNext: Boolean
        var stepNumber: Int
    }

    data class HistoryEntry(var squares: Array<String?>)

    private fun handleClick(i: Int){
        val history = state.history.sliceArray(0..state.stepNumber)
        val current= history.last()
        val squares = current.squares.copyOf()

        if (calculateWinner(squares) != null || squares[i] != null) return

        squares[i] = if(state.xIsNext) "X" else "O"

        setState{
            this.history = history + HistoryEntry(squares)
            xIsNext = !state.xIsNext
            stepNumber = history.size
        }
    }

    private fun calculateWinner(squares:Array<String?>): String? {
        val lines: Array<Array<Int>> = arrayOf(
                arrayOf(0, 1, 2),
                arrayOf(3, 4, 5),
                arrayOf(6, 7, 8),
                arrayOf(0, 3, 6),
                arrayOf(1, 4, 7),
                arrayOf(2, 5, 8),
                arrayOf(0, 4, 8),
                arrayOf(2, 4, 6)
        )

        for (line in lines) {
            val (a, b, c) = line
            if (squares[a] != null && squares[a] == squares[b] && squares[a] == squares[c]) {
                return squares[a]
            }
        }
        return null
    }

    private fun jumpTo (step: Int) {
        setState{
            stepNumber = step
            xIsNext = (step % 2) == 0
        }
    }

    private fun RBuilder.moves () =
        state.history.mapIndexed{ step, _ ->
            val desc = when {
                step != 0 -> "Go to move #$step"
                else -> "Go to game start"
            }

            li {
                button {
                    + desc
                    attrs.onClickFunction = { jumpTo(step) }
                }
            }
        }


    override fun RBuilder.render() {
        val history = state.history
        val current = history[state.stepNumber]
        val winner = calculateWinner(current.squares)
        val status: String =
                when {
                    winner != null -> "Winner: $winner"
                    else -> "Next player: ${if (state.xIsNext) "X" else "O"}"
                }

        div(classes = "game") {
            div(classes = "game-board") {
                board(current.squares){
                    handleClick(it)
                }
            }
            div(classes = "game-info") {
                div { + status}
                ol{ moves ()}
            }
        }
    }
}

fun RBuilder.game() = child(Game::class) {}
```

Stateを使う場合も、Propsのようにしっかり `interface` で定義します。初期化は `init`内部で行います。
Kotlinでは `data class` があるので、今回の `HistoryEntry` のように型を自分で定義することもできます。
初期化を分ける、interfaceでしっかりと型を定義する、などにより、より見通しが良くて安全な状態管理ができます。


## メリット
+ モダンな文法で書ける -> ここまで紹介してきたコードに頻出する `when` などのモダンな文法を使用できます
+ オブジェクト指向の考え方が使える
+ JSXより読みやすい(好みによるかも)
+ 圧倒的安全性
+ バックエンドやモバイルネイティブと同じ言語で書ける -> これはJSも同じなので好みや案件によりそう
+ IntelliJ IDEAの恩恵を全面的に受けられる

## デメリット
+ 学習コストが高い -> オブジェクト指向やJavaから受け継いだ考え方を覚えなければいけない、自由度が高すぎる
+ 開発コストが高い -> 安全性のために型をいちいちしっかり定義しないと行けないので、それを手間に感じるのならおすすめしません
+ 情報がない -> Kotlin/JS自体がかなり最近の技術なので、日本語で書かれた情報は殆どありません、逆に今記事を書けば誰でも先駆者になれます
+ jsライブラリを使いたいときの設定がかなりめんどくさい/情報がない

## まとめ
総合的には、まだまだ難点も多く、実際の案件で運用するのは難しいかもしれませんが、コミュニティを見ているとJetBrainsはかなり本気だと思うので、そのうちTypeScript並に大きくなってくれるのではないでしょうか。

個人的には、文法の表現力が非常に高く、型もきっちり書けるので書いていてとても楽しかったです。

**君もKotlin/JSでモダンにフロントエンド開発をして先駆者になろう！**