TypeScriptでReactを書く(1):JSX


TypeScriptでReactを書きたかったので雑にメモする。

TypeScriptでJSXのコンパイル

Reactはまあ出来るとして、JSXがちゃんとコンパイル出来るのか疑問だったがTypeScript1.6から出来るらしい。2015.8.15現在のバージョンは1.5系なのでnextバージョンをインストールする必要あり。グローバルのtscは別プロジェクトで現行系を使いたいのでlocal installにした。

$ npm install typescript@next --save

JSXをコンパイルするには拡張子を.tsxにする必要があるらしい。あとはコンパイル時に--jsx reactオプションが必要。

もろもろの事情によりタスクランナーはGruntを使っているため、自動コンパイルをしたかったのだが、grunt-typescriptだとどう設定しても.tsxの拡張子は対応してませんと怒られた(設定方法が悪かったのかもしれない)。
追記;grunt-typescript自体にtypescriptが入っていて、それのバージョンが調べた時点で1.5 betaだった。1.5は正式版が出てるので、gruntはgrunt-tsを使ったほうがいいのかもしれない。

一方grunt-tsなら次の設定でコンパイルが出来たのでこれを使うことにした。

  dev: {
    src: [
      'assets/scripts/ts/**/*.ts', 'assets/scripts/ts/**/*.tsx'
    ],
    options: {
      noImplicitAny: false,
      module: 'umd',
      removeComments: true,
      additionalFlags: '--jsx react',
      fast: 'never'
    }
  }

あとは型定義ファイルのインストールも忘れずに

$ dtsm init
$ dtsm install react --save

Reactのビルドのためにbrowserifyを入れたりもするんだけど、その辺りはTypeで書こうが何で書こうが同じなので割愛。

実装

Reactコンポーネントをどうやって書くの?というところで若干詰まった。よく考えるとTypeScriptはES6をしっかりと追っかけているのでES6の例をだいたいそのまま持ってきたら動く。

React0.13から(?)、creatClass()をしなくてもES6 classでReact.componentを継承するとコンポーネントが作れるようになった。この点はTypeScriptも同じ(ジェネリクスだけ付けてあげればOK)。
ポイントはconstructor()をしっかりと実装すること、super()を呼び出すことの2点。

Hello.tsx
/// <reference path="../../../typings/bundle.d.ts" />

import * as React from 'react';

class Hello extends React.Component<any, any> {

  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  onClick() {
    this.setState({ count: this.state.count + 1});
  }

  render() {
    return (
      <div>
        <div>count, { this.state.count }</div>
        <button onClick={ this.onClick.bind(this) }>UP!</button>
      </div>
    );
  }
}

React.render(<Hello />, document.getElementById('app'));

あとはHTMLにタグを埋めたら表示される。

index.html
<div id="app"></div>

あと詰まったポイントはJSX内でthis.onClick.bind(this)としている所。これをついthis.oncClick()と書いてしまっていた。クラス内メソッドで定義されているonClick()なのでHelloにバインドされて呼び出されるかと思っていたら、そりゃDOMから直接(?)メソッドコールされるときはthis変わるよね、という話。