Development

【React-Redux】react-hot-loaderでHMR実装編 [初心者入門7日目]

React × Reduxの連載記事の7回目です。

今回は開発の効率をよくするためにHotReload(通称HMR)の実装とソースマップの出力設定を行いたいと思います。

HotReloadができるようになると、Devサーバーで実行中にソースを修正した時に、ブラウザが再読込(リロード)されることなく、修正箇所だけ画面に反映させることが可能になります。

この辺りも、バージョンによって実装も異なり、Web上には様々な記事が溢れていてどれが最新情報かわからなくなってしまうので、現時点(2020/03/23)での最新のやり方(たぶん)をまとめていきたいと思います。

この記事でわかること
  • react-hot-loaderを使ったHot Reloadの実装方法
  • reduxでのHot Reloadの実装方法

この記事のターゲットとなる環境

現在の環境状況や前提条件を書いておきます。

前提条件
  • Mac OS Catalina ver 10.15.3
  • 6日目の記事内容まで理解している
  • 環境ターゲット(2020-03-23現在最新)
    • React16.13
    • Redux7.2
    • webpack4.42
    • babel7.8.7
    • eslint6.8
    • Material-UI4.9
    • react-router-dom5.1.2
    • react-hot-loader4.12

これまでのReact × Reduxの連載記事はこちらからどうぞ

【2020年4月版】React × Redux 初心者入門 React × Redux の初心者向け連載記事です。 ネットで調べると古いバージョンの記事が溢れているので、できるだけ最新環境...

必要なモジュールをインストールする

HMR(Hot Module Replacement)を実装するにあたり、実装のやり方がいくつかありますが、今回はreact-hot-loaderとmodule.hotを使ってReact ReduxのHMRを実装していきたいと思います。

まずはreactのソースのHMRを可能にするreact-hot-loader@hot-loader/react-domをインストールします。

$ npm install --save react-hot-loader @hot-loader/react-dom

react-hot-loader

react-hot-loaderはReactのLiveリロードを可能にするパッケージです。

個人的には今回のwebpack環境でreact hookを使ったソースのHMRを行うにはreact-hot-loaderがベストかと思います。

ただし、以下の注意点がありますので頭の片隅に置いておくことをオススメします。

React-Hot-Loader is expected to be replaced by React Fast Refresh. Please remove React-Hot-Loader if Fast Refresh is currently supported on your environment.

react-hot-loader

npmのReadMeによると、React-Hot-Loaderは React Fast Refreshに置き換えられる予定のようです。

React Nativeやparcel環境ではすでにFast Refreshのサポートがされているのですが、webpack環境ではまだサポートがされていません。

なので現時点ではReact-Hot-Loaderを使用しますが、いずれFast Refreshに書き換えられることになると思います。

@hot-loader/react-domのインストール

React-Hor-Loaderを使って、hookのreactソースに対応させるためには@hot-loader/react-domのインストールも必要となります。

hookはHMRによって自動更新されるので、useEffect等を使う場合には注意しましょう。

❄️ useState(initialState); // will never updated (preserve state)
❄️ useEffect(effect); // no need to update, updated on every render
❄️ useEffect(effect, []); // “on mount” hook. “Not changing the past”
🔥 useEffect(effect, [anyDep]); // would be updated
🔥 useEffect(effect, [“hot”]); // the simplest way to make hook reloadable

react-hot-loader#Hook support

ReactのHMRを実装する

.babelrcにreact-hot-loader/babelを追加

babelの設定ファイルである.babelrcのpluginnsにreact-hot-loader/babelを追加します。

  "plugins": [
    "react-hot-loader/babel"
  ]

AppContainer.jsxをreact-hot-loaderでラップする

アプリケーションルートになるAppContainer.jsxをreact-hot-loaderのhotでラップします。

import { hot } from 'react-hot-loader/root';
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';
...
...
{途中略}
...
...
export default hot(AppContainer);

ハイライトされている1行目と10行目が追加箇所です。

これにより、AppContainerから配下の依存するモジュールはHMRの監視対象になります。

webpack.config.jsにreact-domのaliasを追加

今回はReactのhooksを利用しているので、@hot-loader/react-domをreact-domにaliasの追加を行います。

webpack.config.jsに以下の1行を追加してください。

 resolve: {
    extensions: ['.js', '.jsx'],
    alias: { 'react-dom': '@hot-loader/react-dom' },
  },

ReactのHMR対応は以上で完了です。

ためにしnpm startでdev-serverを実行してみてください。

その後、実行したままソースを修正して保存すると、ブラウザがリロードされることなく変更が反映されると思います。

react-hot-loaderでhooksの更新を行わせないようにすることも可能です。

import { setConfig } from 'react-hot-loader';
 
setConfig({
  reloadHooks: false,
});

詳しくはこちらを参照ください。

ReduxのHMRを実装する

reactのHMRはreact-hot-loaderにより実装されたと思います。

ただ、このままだとreduxのreduserやslicesの修正を行った時にHMRが行われません。

なのでReduxのHMRを実装するためにmodule.hotを利用してreducerの読み直しをするように変更していきます。

Module Hotについてはこちらを参照ください。

監視対象とするreducerを別出しにする

現時点ではstores/index.jsでsliceのreducerをcombineReducersでrootReducerにまとめていたと思います。

この部分をstores/index.jsからslices/reducers.jsにHMR監視対象のソースとして新たに作成します。

import { combineReducers } from 'redux';

import counter from './counter';

const rootReducer = combineReducers({
  counter: counter.reducer,
});

export default rootReducer;

storesでreducerを監視対象にする

stores/index.jsでreducerがstoreにまとめられていると思います。

先ほどconvineReducersでsliceのreducerをまとめているところを別ファイルに移行したので、その部分を削除し、代わりに作成したslices/reducers.jsを読み込みます。

さらにmodule.hotを利用して、開発環境でhotが有効な場合のみ、slices/reducersを監視して変更があった場合にreducerの書き換えを行うようにしていきます。

import { configureStore } from '@reduxjs/toolkit';

// ↓↓↓↓↓↓slices/reducersに出したので削除
// import counter from '../slices/counter';
//
// const rootReducer = combineReducers({
//   counter: counter.reducer,
// });

// ↓↓↓↓↓↓↓作成したreducersのファイルを読込む
import rootReducer from '../slices/reducers';

const store = configureStore({
  reducer: rootReducer,
});


if (process.env.NODE_ENV === 'development' && module.hot) {
  // Enable Webpack hot module replacement for reducers
  module.hot.accept('../slices/reducers', () => {
    const nextReducer = rootReducer;
    store.replaceReducer(nextReducer);
  });
}

export default store;

これでredux側のソースもHMRが可能になり、実行中に変更があった場合はリロードせずに画面に反映されると思います。

ためにし実行中にslices/couter.jsのincrimentの数値などを変えてみてください。

画面の再描画が行われなくても変更が反映されていると思います。

これでReact Redux環境におけるHMRの実装が完了しました。

HotReload(HMR)の実装編まとめ

今回はReact ReduxでHMRの実装をしてみました。

HMRが正しく動くか動かないかで、開発効率がだいぶ変わってくると思います。

プロジェクトが大きくなればなるほどコンパイルに時間もかかりますし、何より画面をみながら細かい修正や調整が可能になるので、絶対HMR環境を実装するのはオススメです。

この記事ではreact-hot-loaderを使用して実装しましたがModule.hotで実装する方法もあったりしますので、もっと詳しく知りたい人は是非調べてみるといいと思います。

間違っているとこや、わからないところなどはコメント頂ければ嬉しいです。

誰かの参考になれば幸いです。

ソースマップを追加する(おまけ)

現在のままだと、chromeの開発者ツールなどでデバッグする場合や、consoleで吐かれたエラーなどがbundle.jsを参照してしまいます。

それだとデバッグがしづらいので、ソースマップを出力してbundleされる前のファイルを表示したいと思います。

webpack.config.jsの修正

webpack.config.jsに以下の1行を追加します。

devtool: 'inline-source-map',

これでデバッグ時にソースマップの表示されるようになりました。

簡単ですね!

次回予告

次回予告というかやろうと思ってること

  • Redux Toolkitから作成するDucksパターンで実装する[jin_icon_check_circle]完了
  • ESLintでAirbnbのスタイルガイドを実装する[jin_icon_check_circle]完了
  • Materialデザインを実装する[jin_icon_check_circle]完了
  • カスタムCSSを実装する[jin_icon_check_circle]完了
  • ページルーティングを実装する[jin_icon_check_circle]完了
  • 開発環境の効率化をするためにHotReloadとソースマップを実装する[jin_icon_check_circle]完了
  • REST通信を実装する
  • ログイン制御のフローを実装する
  • デプロイ方法を検討、実装する

次の記事はこちら

【React-Redux】axiosでREST API通信実装編 [初心者入門8日目] React×Reduxの連載記事の8回目です。 今回はいよいよAPIとのREST通信を実装していきたいと思います。 Jav...

勉強するならプログラミングスクールがベスト

今からプログラミングを勉強してプロのエンジニアを目指すなら、プログラミングスクールで勉強するのがベストです!

未経験からプロのエンジニアを育てるオンラインブートキャンプ

最短4週間で未経験からプロを育てるオンラインスクールなので、自宅からプログラミングやアプリ開発を学ぶことができます。


オススメ本

POSTED COMMENT

  1. A より:

    こちらのコンテンツにはいつもお世話になっております。ありがとうございます。

    おかげさまでここまで完走することができました。次回も楽しみにしております!
    特に「REST通信を実装する」が楽しみです。また個人的には、TypeScriptの導入や Connected React Router の利用なども学んでみたいです。

    # Amazonの欲しい物リストなどの公開や”投げ銭”のプラットフォームのご利用はされておられませんか?

    • hiro より:

      Aさん。いつもコメントありがとうございます。

      そして投稿が遅れてしまっていて申し訳ないです。
      なるはやで次回を投稿しておきたいと思います。

      TypeScript導入いいですね!個人的にはトレンド的に考えても使っておいて損はないと思いますし、初心者入門連載が落ち着いたらTypeScript記事でも書いてみようと思います。

      Connected React Routerの部分は今回は直接hooksで済ませちゃってますね。
      routerの状態をちゃんとredux管理したいのであれば導入オススメです。

      WishListとかは前のブログには置いておいたんですけど、気が向いたらまたおいておきますw

      これからもどうぞよろしくお願いします!

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です