React+TypeScript+jQueryの共存(1)の続きです。
TypeScriptでJSXを扱う
環境が作れたら、今までReactで作ってきたJSXのコンポーネントを、TypeScriptで書くためにTSXにしていきます。
とりあえず単純にファイルの拡張子を.tsxに変えちゃいます!
もちろんこれだけだと何も動きません。
でも.tsxファイルがあれば、
npm run build
とすることで、ビルド&監視ができます。
拡張子が.tsや.tsxのファイルを修正・保存した時点で毎回自動でビルドしてくれるので、エラーの内容も確認できます。
私はJSXやTSXを修正するときは、Atomにプラグインを入れて使ってます。おかしいところがあったら保存しなくても編集した瞬間にヒントが出るので便利です。
(Atomの方でエラーがなくてもビルドが通るとは限らないのがつらいところですが…あくまでも補助機能です)
で、今までだとエントリポイントのapp.jsxは、こんなふうに書いていました。
//app.jsx var React = require('react'); var ReactDOM = require('react-dom'); var SiteHeader = require('./SiteHeader.jsx'); var ArticleArea = require('./ArticleArea.jsx'); var SiteFooter = require('./SiteFooter.jsx'); //メインコンポーネント var Main = React.createClass({ render: function() { return ( <main> <ArticleArea /> </main> ); } }); //bodyコンポーネント var Body = React.createClass({ render: function() { return ( <div id="container"> <SiteHeader /> <Main /> <SiteFooter /> </div> ); } }); /* React + JSX */ ReactDOM.render( <Body />, document.getElementById('body') );
これが、tsxにしたときはこうです。
//Index.tsx import React = require('react'); import ReactDOM = require('react-dom'); import $ = require('jquery'); import SiteHeader from "./SiteHeader"; import ArticleArea from "./ArticleArea"; import SiteFooter from "./SiteFooter"; //メインコンポーネント class Main extends React.Component<any,any> { render() { return ( <main> <ArticleArea /> </main> ); } } //bodyコンポーネント class Body extends React.Component<any,any> { render() { return ( <div id="container"> <SiteHeader /> <Main /> <SiteFooter /> </div> ); } } /* React + JSX */ ReactDOM.render( <Body />, document.getElementById('body') );
モジュールの読み込みがだいぶ変わったのと、コンポーネントを作る時にReact.createClassじゃなくなりましたね。
ReactやjQueryのモジュールをimport
モジュールの読み込みは、いろんな記事を見ていると違う書き方も見かけたのですが、なんだかんだ最後にうまくいったのはこれでした。
私の使っているTypeScriptのバージョンは1.8.10です。
1.6だったり2.0のbeta版を使っている人なんかもいるので、そういった記事の情報がまぜこぜになってしまい、だいぶ混乱しました(;´Д`)
2.0の場合、@typeっていう型定義の方法があって、それが楽ちんらしいので試してみたのですがうまく導入できませんでした。
うまくいかない原因がそもそもほかのところにあるかもしれないんですけど、それを特定できるようなところまで持っていけないので、前回作った環境でTSDを使っています。
jQueryは、index.htmlで読み込んでいるだけではダメで、ここでモジュールを指定してあげないといけません。
「jQueryの型定義ファイル(/typings/jquery)」「本体のjQueryの読み込み(index.html)」と「モジュールの読み込み(Index.tsx)」すべて揃わないとコンパイルも通らないし、あちこちで「$なんてnameは見つからないよ」みたいなエラーが出まくります。
TypeScriptの真骨頂、classと型定義
さて、肝心のJSXがどうなったかというと。
class Main extends React.Component<any,any> { render() { return ( <main> <ArticleArea /> </main> ); } }
React.Componentから継承して、Mainというclassを作るよって言ってるみたいですね。
<any,any>というのは、<prop,state>の型をそれぞれ指定するところです。
・・・が、
今から型定義を全部やっていくのはつらいので、暫定的にany,any(なんでもアリ)にしちゃいます!
とにかくコンパイルを通して画面を表示できるところまでもっていきたいんです!
ほかのTSXファイルからコンポーネントをもってくる
さて、Index.tsxはできましたが、
import SiteHeader from "./SiteHeader"; import ArticleArea from "./ArticleArea"; import SiteFooter from "./SiteFooter";
このへんで、ほかのTSXファイルからコンポーネントをもってきていますね。
当然、こいつらもTypeScriptに書き換えないといけません。
今回は例として、SiteFooter.tsxの中身を見てみます。
import React = require('react'); //サイトフッタコンポーネント export default class SiteFooter extends React.Component<any,any> { render() { return ( <footer> </footer> ); } }
フッタの中身のHTMLはまだ作っていないので、空のfooter要素を生成するだけです。
class SiteFooter extends React.Component<any,any>は、先ほどと同じですね。
よく見ると、頭に export default というのがついています。
まあ見ての通り、このコンポーネントをexportしておいて、Index.tsx側でimportしている、っていうことですね。
かんたんかんたん(`・∀・´)エッヘン!!
もしここまでで試したければ、一度SiteHeaderとMainを消し、SiteFooterとBodyだけにしておいて
npm run build
しちゃうっていう手もあります。
index.htmlは、こんなふうにReactDOM.render()の対象となるdiv要素と、jQuery本体、common.js、bundle.jsの読み込みが書かれています。
<body> <div id="body"></div> <button type="button" id="overlay"></button> <label id="overlay-close" class="overlay-close"></label> <div id="spinner"></div> <script src="http://code.jquery.com/jquery-2.2.0.min.js"></script> <script src="/js/common.js"></script> <script src="/js/bundle.js"></script> </body>
私が実際にやったときはあちこちエラーが出まくってどこをどうするのが正解か、たどり着くまでにすごく時間がかかりましたが…
これで各コンポーネントをまとめてレンダリングするところまでの大枠はできました。
次回以降、classの中身を見ていきます。