grails-react-boilerplate で React に入門した - 3日目 Babel, ESLint

前回、React Routerを使ったルーティング、サーバーとのデータ通信までできました。しかし、これまでECMAScript 5 (ES5) でJavaScriptを書いてきましたが、grails-react-boilerplateではECMAScript 2015 (ES2015) でJavaScriptが書かれています。

今回はBabelを使って、ES2015に対応できるようにしてみます。

引き続き、以下の記事とリポジトリを参考に進めます。

uehaj.hatenablog.com

github.com

Babel

以前は6to5と呼ばれていたトランスパイラ。JSXも標準でサポート。本稿ではwebpackのローダの一つとして使用

Babelの説明はこう書かれてます。トランスパイラとはコード変換ツールのようなものっていう理解でいいのでしょうか。Babelを使うとES2015のコードをES5に変換できます。ES2015だけでなく、JSXも変換してくれます。

Babelはプラグインを組み合わせて動作させるようです。そして、複数プラグインをセットにしたものをpresetと呼ぶようです。

Plugins · Babel

今回使いたいpresetは、es2015とreactです。

WebpackからBabelを使えるようにしましょう。Webpackのloaderとして、babel-loaderが使えます。

GitHub - babel/babel-loader: Webpack plugin for Babel

Babelのオプションで使用するpresetを選べます。webpack.config.jsにloader: 'babel?presets[]=react,presets[]=es2015'というように書いてもいいですが、.babelrcというファイルに書くこともできます。.babelrcを作成しましょう。

// .babelrc
{
  "presets": ["es2015", "react"]
}

npmでモジュールをインストールします。

$ npm install babel-core babel-preset-es2015 babel-preset-react babel-loader --save-dev

webpack.config.jsでloaderの設定をjsxからbabelへ変更します。

// react-app/hot.webpack.config.js
loaders: ['react-hot', 'babel'],

これで設定ができました!

ES2015の文法はWebで調べるとして、ReactでES2015を使う場合、React.createClassメソッドを使わずにclassとしてReactコンポーネントを定義できます。

Reusable Components | React

BookIndexPageコンポーネントをES2015で書き換えてみます。

// react-app/src/components/BookIndexPage.js
import React, {Component} from 'react';

import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table';
import {Button} from 'react-bootstrap';
import 'react-bootstrap-table/css/react-bootstrap-table-all.min.css';

import * as ajax from '../ajax';
import BookNewDialog from './BookNewDialog';

export default class BookIndexPage extends Component {
  constructor(props) {
    super(props);
    this.state = {booklist: []};
  }
  createBook(creatingBook) {
    this.setState({showNewDialog: false});
    ajax.createBook(creatingBook, (_) => {
      this.reloadData();
    }, (err) => {
      console.log("error");
    });
  }
  reloadData() {
    ajax.getBooks((data) => {
      this.setState({ booklist: data });
    });
  }
  showNewDialog() {
    this.setState({showNewDialog: true});
  }
  hideNewDialog() {
    this.setState({showNewDialog: false});
  }
  componentDidMount() {
    ajax.getBooks((data) => {
      this.setState({ booklist: data });
    });
  }
  render() {
    return (
      <div>
        <h1>Books</h1>
        <Button onClick={this.showNewDialog.bind(this)}>New</Button>
        <BootstrapTable data={this.state.booklist}
                        hover condensed pagination deleteRow
                        selectRow={{
                            mode: 'checkbox',
                            bgColor: "rgb(238, 193, 213)",
                        }}
                        >
          <TableHeaderColumn dataField="id" dataSort={true} isKey={true} >ID</TableHeaderColumn>
          <TableHeaderColumn dataField="title" dataSort={true}>Title</TableHeaderColumn>
          <TableHeaderColumn dataField="price" dataSort={true}>Price</TableHeaderColumn>
        </BootstrapTable>
        <BookNewDialog show={this.state.showNewDialog}
               closeAction={this.hideNewDialog.bind(this)}
               submitButtonAction={this.createBook.bind(this)} />
      </div>
    );
  }
}

webpack-dev-serverを起動してアクセスすると、無事表示されます。

grails-react-boilerplateでは、stage-0というpresetも使っています。これは何でしょうか? 調べてみると、ECMAScriptでは新機能が仕様として認められるかどうかの確度を5段階のStageで表記するようです。

5段階のStage | ECMAScriptとは何か?

Stage0の段階の機能も使いたい場合は、Stage0のpresetも使えるように設定しておく必要があります。

$ npm install babel-preset-stage-0 --save-dev
// react-app/.babelrc
{
  "presets": ["es2015", "stage-0", "react"]
}

ESLint

ESLintはJavaScriptの静的コード解析ツール。早速インストールしてみます。

$ npm install eslint --save-dev

package.jsonにeslintを実行するscriptを追加。

// react-app/package.js
  "scripts": {
    ・・・
    "lint": "eslint src"
  },

これで実行できます。

$ npm run-script lint

たくさんエラーが出ました!ES2015で記述してるので、ESLintにES2015を使ってることを教えてあげないといけないです。ESLintの設定ファイルは .eslintrc です。

ecmaFeaturesでES2015の文法を個別に有効にすることができるようです。env"es6": trueを設定するとmodules以外が有効になるようです(ECMAScript 2015は以前はECMAScript 6という名称で呼ばれてました)。modulesについては個別にecmaFeatures"modules": trueを追加します。JSXも使っているので"jsx": trueも追加。


追記 2016/2/21

ESLintのバージョンが2.xになってから ecmaFeatures の記述を paserOptions に書くようになったみたいです。

"parserOptions": {
  "ecmaFeatures": {
    "jsx": true,
    "modules": true
  }
},

追記ここまで。


Documentation - ESLint - Pluggable JavaScript linter

// react-app/.eslintrc
{
  "env": {
    "es6": true
  },
  "ecmaFeatures": {
    "jsx": true,
    "modules": true
  },
  "rules": {
  }
}

これでnpm run-script lintするとエラーなく実行されます。

.eslintrcにルールを追加していくと色々チェックできるようになります。例えば、セミコロンの省略をチェックするようにするには"semi": 2を追加します。2という数値を指定するとチェックに引っかかった場合にエラーとします。1を指定すると警告です。

List of available rules - ESLint - Pluggable JavaScript linter

// react-app/.eslintrc
"rules": {
  "semi": 2
}

おしまい

今回は、BabelとESLintを導入してみました。ここまでくれば後はモリモリと写経できそうです!