reactjswebpack-dev-serverwebpack-4react-hot-loader

React-Hot-Loader Not Preserving State. v.4.12.21


I followed steps from given https://www.npmjs.com/package/react-hot-loader

Install npm install react-hot-loader

Add react-hot-loader/babel to your .babelrc:

//.babelrc { "plugins": ["react-hot-loader/babel"] }

Mark your root component as hot-exported:

//Button.js

class Button extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 1,
    };
  }

  handleClick = () =>
    this.setState({
      counter: this.state.counter + 1,
    });

  render() {
    return (
      <div>
        <h2> Cool Counter {this.state.counter} !!!!</h2>
        <button onClick={this.handleClick}>{this.state.counter}</button>
      </div>
    );
  }
}

export default hot(Button);

//App.js

import React from 'react';
import { hot } from 'react-hot-loader/root';
import Button from './controls/Button';

const App = () => (
  <div style={{ color: 'purple' }}>
    <Button />
  </div>
);
export default hot(App);

Make sure react-hot-loader is required before react and react-dom:

//Main.js

import 'react-hot-loader';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import './main.scss';

ReactDOM.render(<App />, document.getElementById('container'));

If you need hooks support, use @hot-loader/react-dom

npm install @hot-loader/react-dom

Use webpack aliases

// webpack.config.js
module.exports = {
  // ...
  resolve: {
    alias: {
      'react-dom': '@hot-loader/react-dom',
    },
  },
};

//index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React-Hot-Loader-Test</title>
  </head>

  <body>
    <div id="container"></div>
    <script src="/public/main.js"></script>
    <link href="/public/main.css" rel="stylesheet" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
  </body>

//server.js

var webpack = require('webpack');
var WebPackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');

new WebPackDevServer(webpack(config), {
  publicPath: config.output.publicPath,
  hot: true,
  historyApiFallback: true,
}).listen(8080, 'localhost', function (err, result) {
  if (err) {
    return console.log(err);
  }

  console.log('Listening on localhost:8080');
});

//webpack.config.js

const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production';

module.exports = {
  mode: 'development',
  devtool: 'inline-source-map',
  entry: {
     main: ['react-hot-loader/patch', './src/main.js'],
  },
  resolve: {
    alias: {
      'react-dom': '@hot-loader/react-dom',
    },
    modules: [path.resolve('./src'), path.resolve('./node_modules')],
  },
  output: {
    path: path.resolve(__dirname, 'public'),
    filename: devMode ? '[name].js' : '[name].[chunkhash].js',
    publicPath: '/public/',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,            
        use: ['react-hot-loader/webpack', 'babel-loader'],
        
      },
      {
        test: /\.(sa|sc|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          'css-loader',
          'sass-loader',
        ],
      },
    ],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: devMode ? '[name].css' : 's[name].[hash].css',
      chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
    }),
  ],  
  
};

//.babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-proposal-class-properties", "react-hot-loader/babel"]
}

Finally running 2 command

1. webpack -d
2. node server.js

result: λ node server up and running

i 「wds」: Project is running at http://localhost:8080/
i 「wds」: webpack output is served from /public/
i 「wds」: Content not from webpack is served from G:\Projects\React-Second
i 「wds」: 404s will fallback to /index.html

react hot loading only working for html,css,jsx change

Cool Counter enter image description here

Question: Hooks not storing previous state upon cliking button and then changing any css,html or jsx, UseState or Set state not preserving the state, it always starts with 1.

Please help what did i miss, i'am struggling from hours.

Regards

Shaz


Solution

  • Found the solution

    //webpack.config.js

    const path = require('path');
    const webpack = require('webpack');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    const devMode = process.env.NODE_ENV !== 'production';
    
    module.exports = {
      mode: 'development',
      devtool: 'inline-source-map',
      entry: {
        main: ['webpack-dev-server/client?http://localhost:8080', 'webpack/hot/only-dev-server', './src/main.js'],
        // main: ['react-hot-loader/patch', './src/main.js'],
      },
      resolve: {
        alias: {
          'react-dom': '@hot-loader/react-dom',
        },
        modules: [path.resolve('./src'), path.resolve('./node_modules')],
      },
      output: {
        path: path.resolve(__dirname, 'public'),
        filename: devMode ? '[name].js' : '[name].[chunkhash].js',
        publicPath: '/public/',
      },
      module: {
        rules: [
          {
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            // use: {
            //   loader: 'babel-loader',
            //   // options: {
            //   //   presets: ['@babel/preset-env', '@babel/preset-react'],
            //   // },
            // },
            use: ['react-hot-loader/webpack', 'babel-loader'],
            //use: ['babel-loader'],
          },
          {
            test: /\.(sa|sc|c)ss$/,
            use: [
              {
                loader: MiniCssExtractPlugin.loader,
              },
              'css-loader',
              'sass-loader',
            ],
          },
        ],
      },
      plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin(),
        new MiniCssExtractPlugin({
          // Options similar to the same options in webpackOptions.output
          // both options are optional
          filename: devMode ? '[name].css' : 's[name].[hash].css',
          chunkFilename: devMode ? '[id].css' : '[id].[hash].css',
        }),
      ],
    
      // devServer: {
      //   contentBase: path.join(__dirname),
      //   compress: false,
      //   port: 8080,
      //   historyApiFallback: true,
      //   watchContentBase: false,
      //   publicPath: '/public/',
      // },
    };