jtwp470’s blog

日記とかプヨグヤミングとか

TypeScriptとWebpackとReactを使って簡単なTODOリストを作ってみた

最近フロントエンドの勉強をはじめました。フロントエンド界隈(JavaScript界隈)は変化が激しいようで今最新だーと思っていた技術も数ヶ月後には古いみたいなのがざらにあるようですが、最近私が興味を持っているTypeScriptとFacebookが作っているReact.jsをもとにWebpackを組み合わせて使ってみたというものです。

環境構築

nodeやnpmは既に入っているという前提にしておく。

$ npm init

で出てくる質問に片っ端から答える。yesをするとpackage.jsonが生み出される。

次にTypeScriptをインストールし、トランスコンパイル設定ファイルを生成する。

$ npm install typescript
$ (npm bin)/tsc --init

すると配下にtsconfig.jsonが生成されているはず。 また、tsconfig.jsonはとりあえず以下のように書き直しておく。

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "jsx": "react",
    "noImplicitAny": false,
    "sourceMap": false
  }
}

次にWebpackをインストールする。WebpackはJavaScriptCSSなどのモジュール依存関係を解決しながら変換してくれるものらしい。

$ npm install webpack ts-loader -D

次にWebpackの設定をJavaScriptでかく。webpack.config.jsという名前で保存しておく。

const path = require('path');

module.exports = {
    entry: {
        app: "./index.ts",
    },
    output: {
        path: path.resolve(__dirname, "./dist"),
        filename: "[name].bundle.js",
    },
    resolve: {
        extensions: ["*", ".ts", ".tsx", ".js", ".jsx"],
    },
    module: {
        rules: [
            {
                test: /.tsx?$/,
                use: [{ loader: "ts-loader" }],
            },
        ],
    },
};

次に, Reactをインストールする。

$ npm install react react-dom @types/react @types/react-dom -S

後ろの @types から始まる部分のものは、すべてTypeScriptを使う際に必要でこれらは型定義となっている。

Hello, World

ここまで来たら、Hello Worldと出力させる部分だけをやってみる。

まず、index.htmlを以下のように作成する。

<!DOCTYPE html>
<html>
    <head>
        <title>Tiny TODO app</title>
    </head>
    <body>
        <div id="app"></div>
        <script src="./dist/app.bundle.js"></script>
    </body>
</html>

次に、index.tsを作成する。

import * as React from "react";
import * as ReactDom from "react-dom";
import TodoApp from "./todoapp";

ReactDom.render(
    React.createElement(TodoApp),
    document.getElementById("app")
);

最後にTODOリストを定義するファイルとして、todoapp.tsxを作成する。

import * as React from "react";

export default class TodoApp extends React.Component<any, any> {
    render() {
        return (
            <div>Hello, World</div>
        );
    }
}

これで、webpackを使ってコンパイルしてみる。

$ $(npm bin)/webpack -p

こうしてから普通にindex.htmlへブラウザからアクセスしてみる。

f:id:jtwp470:20170430214006p:plain

こんな感じ。

TODOリストを作成してみる

まず入力フォームを作っておく。入力フォームは以下のような感じ。

import * as React from "react";

export default class TodoApp extends React.Component<any, any> {
    render() {
        return (
            <div>
                <h3>TODO</h3>
                <TodoList items={this.state.items} />
                <form onSubmit={this.handleSubmit}>
                    <input onChange={this.handleChange} value={this.state.text} />
                    <button>{'Add #' + (this.state.items.length + 1)}</button>
                </form>
            </div>  
        );
    }
}

一応できたけど、またstateの値やhandleSubmit, handleChangeと言った部分が実装されていない。

まず、コンストラクタを実装する。

    constructor(props: any) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
        this.state = {items: [], text: ''};
    }

こんな感じ。次にhandleSubmitとhandleChangeをそれぞれ書く。

handleChange(e: any) {
        this.setState({text: e.target.value});
    }
    handleSubmit(e: any) {
        e.preventDefault();
        let newItem = {
            text: this.state.text,
            id: Date.now()
        };
        this.setState((prevState) => ({
            items: prevState.items.concat(newItem),
            text: ''
        }));
    }

最後に今まで入力した値をきちんと出力するためのクラスとして TodoList を定義する。

class TodoList extends React.Component<any, any> {
    render() {
        return (
            <ul>
                {this.props.items.map(item => (
                    <li key={item.id}>{item.text}</li>
                ))}
            </ul>
        );
    }
}

これでおしまい。実際に実行してみる。Webpackでコンパイルした後、ブラウザで普通にindex.htmlを開いてみるとこんな感じになっているはず。

f:id:jtwp470:20170430221833p:plain

ちなみに書いたコードとかはこれ。

github.com

まとめ

環境構築あたりがものすごくしんどいという印象。フロントエンドが正しく動いているというようなテストを書くにはどうするのかまだよくわからないので調べていきたいと思っております。はい。

まだTypeScript自体も写経と言うような感じで使いこなせているわけではないので精進していきたいところです。