3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React + Redux + TypescriptでHMR

Posted at

概要

React + ReduxでTypescriptを使ってHot Module Replacement (HMR)する際のメモ。
なんでこれでOKなのかは詳しく調べないと分からず。
最小構成のプロジェクトを別途作成する。
Webpack-dev-serverを使う前提

準備

npm install --save-dev babel-loader @babel/core react-hot-loader @types/webpack-env
モジュール 導入理由 補足
babel-loader react-hot-loaderが使う *1
@babel/core babel-loaderが利用
react-hot-loader Reactのstateを維持できるようにする
@types/webpack-env hot.moduleの型を使えるように
*1 TypeScriptだとbabel必要なかったのだが仕方なし。

Webpackの設定

webpack.config.js
const path = require('path');
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') .BundleAnalyzerPlugin;

module.exports = (env, argv) => {
  let config = {
    performance: { hints: false }, 
    entry      : { app:[
      // 必要性はよくわからず。
      'webpack-dev-server/client?http://localhost:8080', 
      // 必要性はよくわからず
     'webpack/hot/only-dev-server',
      './dist/index.js'
    ]},
    output: {
      path    : path.join(__dirname,'public/js'),
      filename: '[name].js',
      publicPath: '/js/'
    },
    plugins: [
      // NamedModulesPluginはなくていいみたい。
      //new webpack.NamedModulesPlugin(),
      new webpack.HotModuleReplacementPlugin(), 
    ],
    devServer: {
      historyApiFallback: true,
      contentBase       : path.join(__dirname,'public'),
      port              : 8080,
      hot               : true,
      publicPath        : '/js/',
      watchOptions      : {
        aggregateTimeout: 300,
        poll            : 1000
      }
    },
    resolve: {
      modules: [path.resolve(__dirname, './dist'), 'node_modules'],
      alias  : {
           //省略
      },
      extensions: [".ts", ".tsx", ".js", ".json"]
    },
    module: {
      rules: [
        {
          exclude: /node_modules/,
          test   : /\.js$/,
          //loader : ['react-hot-loader/webpack', 'babel-loader'] 
          //hot-loader/webpackはいらない様子。
          // 入れるとmonorepo全部にhot-loaderのinstallを求められた。
          loader : ['babel-loader']
        }
      ]
    }
  };

  if (argv.mode === 'production') {
    //本番環境専用の設定
    //ファイルサイズのアナライズを実行
    //config.plugins.push(new BundleAnalyzerPlugin());
  } else {
    //開発環境専用の設定
    config.cache = true;
    config.devtool = 'inline-source-map';
  }

  return config;
};

Componentの設定

index.tsx
import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';

ReactDOM.render(<App />, document.querySelector('#root'));
App.tsx
import React from 'react';
import { Provider } from 'react-redux';
import { configureStore } from './ConfigureStore';
import Navigator from './Navigator';

const store = configureStore();
const App = () => {
  return (
    <Provider store={store}>
        <Navigator />
    </Provider>
  );
};

export default App; //Appをhot()で囲ったら動かなかった。Providerの内側にする必要があるみたい。
Navigator.tsx

import React from 'react';
import { hot } from 'react-hot-loader/root';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import AuthPage from './AuthPage';
import TopPage from './TopPage';

const Navigator = () => {
  return (
    <BrowserRouter>
      <Switch>
         <Route exact={true} path='/login' component={AuthPage} />
         <Route exact={true} path='/top' component={TopPage} />
      </Switch>
    </BrowserRouter>
  );
};

export default hot(Navigator); // なのでNavigatorをhotで括った

Reducerの設定

StoreConfigure.ts
import { createStore, combineReducers, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';

// 後述する通り、RootReducerはexport defaultした方が良さそう
import rootReducer from './RootReducer';

// Sagaもif module.hotしないと変更に追従できなそう。
import rootSaga from './RootSaga';

export const configureStore = (initialState = {}) => {
  if (module.hot) {
    module.hot.accept('./RootReducer', () => {
      console.log('module.hot.accept');
      // RootReducerはexport defaultしてるので、require.default
   // RootReducerをexportにして、requireに変えたら動かなかった様子。
      const nextRootReducer = require('./RootReducer').default;
      store.replaceReducer(nextRootReducer);
    });
  }

  const sagaMiddleware = createSagaMiddleware();
  const store = createStore(rootReducer, initialState, applyMiddleware(sagaMiddleware));
  sagaMiddleware.run(rootSaga);

  return store;
};

GitHub

未作成

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?