【React入門13】WDSのHot Module Replacementを使ってみる

f:id:nekorokkekun:20190906110227p:plain:w1000
こちらの記事は、React入門チュートリアルです。

Reactが初めての私がReactを実装していく流れとなるため、初心者の方のつまずきやすいポイントを一通りフォローアップできる内容となっています。

Hot Module Replacementの有効化

編集されたコードのモジュールのみを更新するHot Module Replacementを使用するためには、webpack.config.jsにコードを記述しなければいけません。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); //追記

module.exports = {
    // watchモードを消しておく、またはfalseにしておく
    watch: false,
    mode: 'development',
    entry: './src/index',
    output: {
        path: path.resolve(__dirname, 'public'),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.txt$/,
                use: 'raw-loader'
            },
            {
                test: /\.m?js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                  loader: 'babel-loader',
                  options: {
                    presets: ['@babel/preset-env']
                  }
                }
              }
        ]
    },
    plugins: [
        // プラグインの設定追加
        new HtmlWebpackPlugin({
            title: 'Plugin generate page',
            template: 'src/root.html'
        }),
        new webpack.HotModuleReplacementPlugin() //追記
    ],
    //以下を追記
    devServer: {
        contentBase: path.join(__dirname, 'public'),
        port: 9000,
        hot: true
    }
};

次に以下の2つのファイルを記述しましょう。
src/index.js

function component() {
    const element = document.createElement('div');
    const btn = document.createElement('button');
    const myAlert = require('./components/myAlert').default;

    btn.innerHTML = 'Click me';
    btn.onclick = myAlert;

    element.appendChild(btn);
    return element;
}

let element = component();
document.body.appendChild(element);

if (module.hot) {
    module.hot.accept('./components/myAlert.js', () => {
        document.body.removeChild(element);
        element = component();
        document.body.appendChild(element);
    })
}

src/components/myAlert.js(新規ファイル)

export default function myAlert() {
    alert('bar');
}

myAlert.jsを編集した後に更新を行なっていなくても、Alertのメッセージが変わっていることがわかります。
f:id:nekorokkekun:20190906101253g:plain:w1000

解説をしていきます。

function component() {
    const element = document.createElement('div');
    const btn = document.createElement('button');
    const myAlert = require('./components/myAlert').default;

    btn.innerHTML = 'Click me';
    btn.onclick = myAlert;

    element.appendChild(btn);
    return element;
}

まずこちらのコードで、実行されるとdiv要素とbtn要素が描画される関数を定義しています。

let element = component();
document.body.appendChild(element);

そして、実際にcomponent関数を実行。
これで画面表示がなされるはずです。

if (module.hot) {
    module.hot.accept('./components/myAlert.js', () => {
        document.body.removeChild(element);
        element = component();
        document.body.appendChild(element);
    })
}

最後に「もしモジュールにHot Module Replacementが適用されたら」という条件で、

  • divとbtn要素を描画するelementを排除し、
  • 新たなelement要素を追加する

といった処理を行わせます。

結果として、myAlert.jsの中身が変わっても、ホットリロードされるというわけです。

ReactにおけるHot Module Replacement

Reactはアトミックデザイン、コンポーネントベースを取り入れたFWのため、webpackのwatchモードといったように「アプリ全体がリロードされてしまう」と困ることが多いようです。

例えば、タブ選択ができるようなアプリを開発している時に、「タブ2」の画面を見ながら開発を続けたくても、全体がリロードされてしまうとコードが編集されるたびにデフォルトタブに戻ってしまうわけです。

Hot Module Replacementは先ほども述べたように、編集されたファイルのみを更新してくれるため、画面全体が再描画されることはありません。

ReactにおけるHot Module Replacementを体験してみる

では実際にReactにおけるHot Module Replacementを体験してみましょう。

src/index.js

import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './components/hello';

class App extends Component {
    constructor() {
        super();
        this.handleChange = this.handleChange.bind(this);
        this.state = {
            flag: false
        }
    }
    handleChange() {
        this.setState({
            flag: !this.state.flag
        })
    }
    render() {
        return (
            <div>
                <button onClick={this.handleChange}>Switch Flag</button>
                {`${this.state.flag}`}
                <Hello/>
            </div>
        )
    }
}

render(
    <App />,
    document.querySelector('#root')
);


src/components/hello.js

import React from 'react';

const Hello = (props) => {
    return <div>not reloaded!</div>
}
export default Hello;

これで画面上にはボタンの横にfalseと表示され、その下には「non reloaded!」と書かれているはずです。

ボタンを押してみましょう。
f:id:nekorokkekun:20190906105544g:plain:w700

下の「non reloaded!」は再描画されていないことがわかります。
これがReactにおけるHot Module Replacementの利点です。

また、Reactではreact-hot-loaderというプラグインが提供されているので、Reactのアプリケーション作成時に簡単に導入することができます。
github.com