Reactのチュートリアルをもう一回やってみた(ESLint & Webpack)

Reactのチュートリアルをもう一回やってみた(ESLint & Webpack)ReactのチュートリアルをTypeScripで書いてみたんですが、やっぱりES6(ES2015)版で書いた方がいいかもということでもう一度チュートリアルをやってみることにしました。今回はコンパイルをwebpackにやらせようと思います。コードのチェックはESLintにやってもらって、スタイルはAirbnbのReact/JSXスタイルガイドに沿ってやろうと思います。

最近やったReactの学習

準備

まずは、各種ライブラリを入れたりします。React

npm install --save react react-dom

ESLint

$ npm install -g eslint
$ eslint -v
$ eslint --init

Atomで書いているのですが、linter-eslintを使ってみることにしました。ES6も見てくれるということで。入れていなかったのでインストールする。

eslint --initすると対話的に環境を聞いてくれてファイルを作ってくれるのでありがたい。React使いますかとか聞かれる。そしてできたのがこれ。必要に応じてこのファイルで設定するらしい。

.eslintrc.json

{
  "rules": {
    "indent": [
      2,
      2
    ],
    "quotes": [
      2,
      "double"
    ],
    "linebreak-style": [
      2,
      "unix"
    ],
    "semi": [
      2,
      "always"
    ],
    "no-unused-vars": [
      1, {"vars": "all", "args": "after-used"}
    ],
    "no-console": 1
  },
  "env": {
    "es6": true,
    "browser": true
  },
  "extends": "eslint:recommended",
  "ecmaFeatures": {
    "jsx": true,
    "experimentalObjectRestSpread": true,
    "modules": true
  },
  "plugins": [
    "react"
  ]
}

Webpack

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

webpack.config.jsJavaScript – webpack+babel環境でフロントエンドもES6開発を参考にします。といっても、やりながらだいぶ変わりました。最終的にはこうなりました。

module.exports = {
  entry: __dirname + "/src/app.js",
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel",
        query:{
          presets: ['react', 'es2015']
        }
      },
      {
        test: /\.jsx$/,
        exclude: /node_modules/,
        loader: "babel",
        query:{
          presets: ['react', 'es2015']
        }
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  }
};

簡易サーバーと変更監視。

今回はwebpackでjsの変更を監視しつつ、変更があったら簡易サーバーで立ち上げたページが自動更新するという風にしたいと思います。そのために、lite-serverconcurrentlyを追加します。

$ npm install lite-server concurrently --save-dev

package.jsonのところに、lite-serverとwebpackとconcurrentlyに関する記述を追加します。npm startするとes6をjsにしてさらにウェブページが更新されます。

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "webpack": "webpack -w",
  "lite": "lite-server --verbose --open dist",
  "start": "concurrent \"npm run webpack\" \"npm run lite\""
},

Reactのチュートリアル二回目

さて、環境が整ったところでReactもう一度チュートリアル読みながら書いていきます。といっても大筋はうまくいくのでハマったところを書いていきます。

import CommentList from "./CommentList.jsx";
import CommentForm from "./CommentForm.jsx";

他のファイルを読み込む際に拡張子を入れないとモジュールが見つからないと言われました。Suggestion: Webpack require should resolve jsx by default · Issue #16 · newtriks/generator-react-webpackによるとresolve…が必要ということだったのでつけたらうまくいきました(冒頭のwebpackの設定ファイルに反映しています)。

その他、特に大きな嵌りなく完成させることができました。完成品はこちらに置いておきます。

js-study/9.react-tutorial-2 at master · morizotter/js-study

ESLintのおかげで記法もそんなにぶれずに書けたと思います。ただし、正しい記法とかもっと良い記法はもちろんあると思います。JS慣れてないので。。。

完成品のコードを貼ってみます。

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="text/javascript" src="dist/bundle.js"></script>
  </body>
</html>

app.js

import React from "react";
import ReactDOM from "react-dom";
import CommentBox from "./CommentBox";

ReactDOM.render(
  ,
  document.getElementById("app")
);

CommentBox.jsx

import React from "react";
import request from "superagent";
import CommentList from "./CommentList";
import CommentForm from "./CommentForm";

export default class CommentBox extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: []
    };
  }

  loadCommentsFromServer() {
    request
      .get(this.props.url)
      .end((err, res) => {
        if (err) {
          throw err;
        }
        this.setState({data: res.body});
      });
  }

  handleCommentSubmit(comment) {
    var comments = this.state.data;
    comment.id = Date.now();
    var newComments = comments.concat([comment]);
    this.setState({data: newComments});
    request
      .post(this.props.url)
      .send(comment)
      .end((err, res) => {
        if (err) {
          this.setState({data: comments});
          throw err;
        }
        this.setState({data: res.body});
      });
  }

  componentDidMount() {
    this.loadCommentsFromServer();
    setInterval(this.loadCommentsFromServer.bind(this), this.props.pollInterval);
  }

  render() {
    return (
      

Comments

); } }

CommentList.jsx

import React from "react";
import Comment from "./Comment";

export default class CommentList extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    var commentNodes = this.props.data.map((comment) => {
      return (
        
          {comment.text}
        
      );
    });

    return (
      
{commentNodes}
); } }

CommentForm.jsx

import React from "react";

export default class CommentForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      author: "",
      text: ""
    };

    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleAuthorChange = this.handleAuthorChange.bind(this);
    this.handleTextChange = this.handleTextChange.bind(this);
  }

  handleAuthorChange(e) {
    this.setState({author: e.target.value});
  }

  handleTextChange(e) {
    this.setState({text: e.target.value});
  }

  handleSubmit(e) {
    e.preventDefault();
    var author = this.state.author.trim();
    var text = this.state.text.trim();
    if (!text || !author) {
      return;
    }

    this.props.onCommentSubmit({author: author, text: text});
    this.setState({author: "", text: ""});
  }

  render() {
    return (
      
); } }

Comment.jsx

import React from "react";
import marked from "marked";

export default class Comment extends React.Component {
  rawMarkup() {
    var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
    return { __html: rawMarkup};
  }
  render() {
    return (
      

{this.props.author}

); } }

今回の学習で利用したサービス・ツールなど

下記の記事を参考にしました

終わりに

とりあえず、ほんとうに基本的な部分はやったのですが、繰り返してやって雰囲気つかめてきました。あとは、Awesomeでも見ながら慣れていこうと思います。とりあえず、Radium使ってみたい。

Pocket
LINEで送る

You may also like...

  • morizotter

    ありがとうございます!教えていただいた内容を反映させて記事を更新しました!

  • 長島徹

    ES6+jsx の場合、babel-eslint は不要です。
    babel-eslint はダーティ ハックの塊なので、なるべくなら使わないほうがいいかなと、中の人的には思います。
    あと .eslintrc に ecmaFeatures.modules が足りないように思います。