gaearon / react-hot-loader

Tweak React components in real time. (Deprecated: use Fast Refresh instead.)
http://gaearon.github.io/react-hot-loader/
MIT License
12.26k stars 801 forks source link

React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work. #1227

Closed rybon closed 5 years ago

rybon commented 5 years ago

What is the actionable thing to do here? I've followed the required steps to set it up, but still see this warning.

HashemKhalifa commented 5 years ago

you need to add the matched version for react-hot-dom in your package file

"@hot-loader/react-dom": "^16.8.6",

and in your webpack config, you need to add

 alias: { 'react-dom': '@hot-loader/react-dom'  }
rybon commented 5 years ago

Okay, that indeed resolves the issue. But shouldn't that be added as a step 4? The readme is confusing about this. It says a Webpack plugin is required, then recommends using the Babel plugin instead. The Webpack plugin apparently patches React DOM for you to basically turn it into @hot-loader/react-dom, but it is unclear if it can be used in conjunction with the Babel plugin. Can both plugins be used at the same time or will that lead to conflicts or other downsides?

rybon commented 5 years ago

Using the Webpack plugin in conjunction with the Babel plugin does not remove the warning. Solely using the Webpack plugin doesn't either. Only the Babel plugin combined with @hot-loader/react-dom and a Webpack config alias does.

Macil commented 5 years ago

For me, just using the webpack plugin (with include: /node_modules/ so it only runs on node_modules(?) -- maybe you're missing this part?) and the babel plugin together seems to make everything work correctly, including making that warning go away.

I'm also really confused by the readme. I'm slightly worried that I might have things set up incorrectly and only working by coincidence and that it might break on an update.

rybon commented 5 years ago

I probably forgot include: /node_modules/ for the Webpack plugin. Will try that.

theKashey commented 5 years ago

Usually, it's not about include, but exclude. There are 3 ways to set up "hot-patch", and webpack-loader is working only for webpack, while it's not the only one bundler one could use.

PS: Feel free to change README to be more clear and understandable.

rybon commented 5 years ago

The Webpack plugin with include: /node_modules/ indeed does the trick. @hot-loader/react-dom and the Webpack config is alias is no longer required in that case.

@theKashey is it okay to use the Babel and Webpack plugins at the same time? Or are they meant to be used separately?

theKashey commented 5 years ago

That's ok, they are taught to be friends :) They were not before webpack-loader got ability to patch react-dom and probably some pieces of that past are still among readme.

HashemKhalifa commented 5 years ago

@rybon would you kindly share the config with us?

rybon commented 5 years ago

Sure.

// App.jsx

import { hot } from 'react-hot-loader/root'

// ...

export default hot(App)
// .babelrc

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

{
  "dependencies": {
    "react-hot-loader": "^4.8.3",
    "@hot-loader/react-dom": "^16.8.6"
    // ...
  }
  // ...
}
// webpack.config.js

{
  // ...
  resolve: {
    alias: {
      'react-dom': '@hot-loader/react-dom'
    }
  }
}
rybon commented 5 years ago

Alternative:

// App.jsx

import { hot } from 'react-hot-loader/root'

// ...

export default hot(App)
// package.json

{
  "dependencies": {
    "react-hot-loader": "^4.8.3"
    // ...
  }
  // ...
}
// webpack.config.js

{
  // ...
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        include: /src/
      },
      {
        test: /\.(js|jsx)$/,
        use: 'react-hot-loader/webpack',
        include: /node_modules/
      }
      // ...
  ]
}

Notice:

rybon commented 5 years ago

Now the question is, which configuration is preferable? What are the downsides of the alternative configuration? That is not clear to me. Personally, I prefer the alternative configuration, because it requires less configuration. But if it does not work as properly as the Babel approach, I'd rather use that. When using the Webpack approach, should the .babelrc plugin be used as well? Or can it safely be omitted?

HashemKhalifa commented 5 years ago

@rybon I agree with you it's quite confusing for me as well and the first approach doesn't work as expected because of react-dom alias. and the babel approach working perfectly fine for me.

btw you can use babel-config.js instead of .babelrc if you are using babel 7

rybon commented 5 years ago

Okay. I just noticed when using the Webpack approach without @hot-loader/react-dom that in some cases the HMR only works the first time, but not in subsequent times. Adding the .babelrc / babel-config.js plugin back in resolves that issue.

So, I guess the recommendation would be to always use the Babel plugin? And then decide to go with either the Webpack plugin or the @hot-loader/react-dom and the Webpack resolve.alias setting.

theKashey commented 5 years ago

You have to use babel-plugin or webpack-loader to make RHL work. @hot-loader/react-dom in any form - is just an addition.

milanvdm commented 5 years ago

How would you set this up using Parcel? Because I have the babel-plugin but still see this warning.

kpopovic commented 5 years ago

Hello, a get this message

"React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work" when using https://parceljs.org/ bundler. Can this message be fixed for https://parceljs.org/ ?

This is how I tried to load App (using Hooks):

import { hot } from 'react-hot-loader' import React from 'react' ... export default hot(module)(App)

is there any working example using Parcel + React Hooks ?

Best Regards, Krešimir

theKashey commented 5 years ago

For parcel use hot-loader/react-dom

Use alias field in package.json to rewire your project. This will affect dev and production modes. See https://github.com/parcel-bundler/parcel/pull/850 { "alias": { "react-dom": "@hot-loader/react-dom" } }

kpopovic commented 5 years ago

Hello @theKashey , now it is fine. No WARN message shown.

how I start the web app: NODE_ENV=development parcel -p 3500 assets/index.html --open

package.json snippet: ... "alias": { "react-dom": "@hot-loader/react-dom" }, ....

karlitos commented 5 years ago

Hi, I suffer from the same warning as other. I do not use webpack, I work on a electron app which utilizes the electron-compile.

If I use yarn (yarn add react-dom@npm:@hot-loader/react-dom) I do net get the warning, but it adds

"react-dom": "npm:@hot-loader/react-dom",

to my package.json which is something npm is not compatible with.

npm ERR! code EUNSUPPORTEDPROTOCOL npm ERR! Unsupported URL Type "npm:": npm:@hot-loader/react-do

I would like to stick with npm instead of yarn, but I am not able to get rid of the warning with my setup:

"dependencies": {
    "@hot-loader/react-dom": "^16.8.6",
    "electron-compile": "^6.4.4",
    "electron-devtools-installer": "^2.1.0",
    "electron-squirrel-startup": "^1.0.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-hot-loader": "^v4.8.4"
  },
  "devDependencies": {
    "babel-plugin-transform-async-to-generator": "^6.24.1",
    "babel-plugin-transform-es2015-classes": "^6.24.1",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",
    "electron-forge": "^5.2.4",
    "electron-prebuilt-compile": "4.0.0",
    "eslint": "^3",
    "eslint-config-airbnb": "^15",
    "eslint-plugin-import": "^2",
    "eslint-plugin-jsx-a11y": "^5",
    "eslint-plugin-react": "^7"
  },
  "alias": {
    "react-dom": "@hot-loader/react-dom"
  }

It says, that .compilerc (the electron-compile config) also accepts environments with the same syntax as .babelrc. My .compilerc looks like:

{
  "env": {
    "development": {
      "application/javascript": {
        "presets": [
          ["env", { "targets": { "electron": "1.6.0" } }],
          "react"
        ],
        "plugins": ["transform-async-to-generator", "transform-es2015-classes", "react-hot-loader/babel"],
        "sourceMaps": "inline"
      }
    },
    "production": {
      "application/javascript": {
        "presets": [
          ["env", { "targets": { "electron": "1.6.0" } }],
          "react"
        ],
        "plugins": ["transform-async-to-generator", "transform-es2015-classes"],
        "sourceMaps": "none"
      }
    }
  }
}

I tried to add the es2017-node7 babel, preset, but still no luck.

I also tried to use babel-plugin-module-resolver and changed the development section of my .compilerc to:

...
"plugins": ["transform-async-to-generator", "transform-es2015-classes", "react-hot-loader/babel",
        ["module-resolver", {
              "alias": {
                 "react-dom": "@hot-loader/react-dom"
              }
            }]
        ],
...

Still no luck 😢

Is there a way to get react-hot-reload to work without needing to use yarn ?

theKashey commented 5 years ago

You can always “ln” real directories.

karlitos commented 5 years ago

You can always “ln” real directories.

Thank you, I tried this already and it works. But this approach makes a local development setup quite difficult. 🤕

The issue with react-🔥-dom not being used properly is caused by babel-plugin-module-resolver and the fact babel is ignoring _nodemodules by default.

The electron-compile project is marked as deprecated by now, so there won't be probably any support for setting aliases for npm modules in the future.

I tried a different approach with setting an environment variable in the script for the local development and using the right ReactDOM respectively:

import 'react-hot-loader';
import React from 'react';
import ReactDOM from 'react-dom';
import PatchedReactDOM from '@hot-loader/react-dom';
import App from './components/App';

if (process.env.REACT_DOM === 'patched') {
  PatchedReactDOM.render(<App />, document.getElementById('App'));
} else {
  ReactDOM.render(<App />, document.getElementById('App'));
}

but although the if-else is being resolved properly, I still get the warning.

theKashey commented 5 years ago

Not, it would not work exactly this way - by default RHL would patch something names react-dom. You might patch another thing, calling ReactHotLoader.patch(React, PatchedReactDOM); manually, but it would not remove the message.

Probably the best option is to move this message from configure time to a render time(AppContainer constructor), giving you options to suppress or fix it.

theKashey commented 5 years ago

v4.8.5 gives two options:

  1. ReactHotLoader.patch(React, PatchedReactDOM);, error shall not be displayed
  2. setConfig({showReactDomPatchNotification: false}), error would not be displayed.
karlitos commented 5 years ago

Hello Anton,

sorry for bothering you again, but I am more confused than ever. I do not want to hide the warning but set-up my configuration right.

I updated react-hot-loader to 4.8.5. My current entry point file looks like:

import ReactHotLoader from 'react-hot-loader';
import PatchedReactDOM from '@hot-loader/react-dom';
import ReactDOM from 'react-dom';
import React from 'react';
import App from './Front-End/App';

ReactHotLoader.patch(React, PatchedReactDOM);
ReactDOM.render(<App />, document.getElementById('App'));

The warning message about react-🔥-dom patch is gone, but in the terminal I can see: React-Hot-Loader: Hot Module Replacement is not enabled

This is electron App, so I also tried the "old" pattern:

import ReactHotLoader, { AppContainer } from 'react-hot-loader';
import PatchedReactDOM from '@hot-loader/react-dom';
import ReactDOM from 'react-dom';
import React from 'react';
import App from './Front-End/App';

ReactHotLoader.patch(React, PatchedReactDOM);

const render = () => {
  ReactDOM.render(<AppContainer><App /></AppContainer>, document.getElementById('App'));
};

render();
if (module.hot) {
  module.hot.accept(render);
}

but I still get the React-Hot-Loader: Hot Module Replacement is not enabled message in the terminal.

Also, eslint complains about ReactHotLoader import: Default export is not declared in imported module

theKashey commented 5 years ago

React-Hot-Loader: Hot Module Replacement is not enabled

(!module.hot) {console.error('React-Hot-Loader: Hot Module Replacement is not enabled'); So module.hot is not defined - It's hard to guess why.


ReactHotLoader.patch(React, PatchedReactDOM); ReactDOM.render(<App />, document.getElementById('App'));

THIS IS WRONG CODE. The idea is to USE another version of react-dom.

ReactHotLoader.patch(React, PatchedReactDOM);
PatchedReactDOM.render(<App />, document.getElementById('App'));

Also, eslint complains about ReactHotLoader import: Default export is not declared in imported module

The question - how it might know it.

mrfelton commented 5 years ago

After reading through this entire thread, and the readme several times I'm no clearer on the recommended way to set this up.

Readme is super confusing and there is so much conflicting information scattered throughout this repository.

theKashey commented 5 years ago

@mrfelton - could you elaborate a bit more. There is always some room for improvement and I am keen to hear was is not clear and what is the problem you are solving.

mrfelton commented 5 years ago

Hi, sure. So on the surface my issue is as per the title of this issue. I see the error :"React-Hot-Loader: Hot Module Replacement is not enabled" in my logs (even though HMR does seem to be working).

But the deeper issue is that the recommended config as detailed at the top of the readme does not work. The only way I have been able to get things to work is by cobbling together a config that is based on various suggestions by frustrated users on github, parts of the readme that are not listed as part of the recommended setup, and pure trial and error.

I don't know if I have things set up correctly. Based on the readme and the error in my console would indicate that I don't, yet HMR does work.

There are several different elements to setting up the configuration and I don't really understand which ones are needed and which are not, and more specifically why one is needed over the other, the relationship between them and what they actually do.

My app is an electron app and it's using webpack.

Here are the key parts of my setup (if you want to look at the actual config or try it for yourself, the project is https://github.com/LN-Zap/zap-desktop/tree/next):

package.js

  "dependencies": {
    "@hot-loader/react-dom": "16.8.6",
    "react": "16.8.6",
    "react-dom": "16.8.6",
    "react-hot-loader": "4.8.8",
}

index.js

import React from 'react'
import ReactDOM from 'react-dom'

const render = Component => {
  ReactDOM.render(
    <Provider store={store}>
      <Component history={history} />
    </Provider>,
    MOUNT_NODE
  )
}

render(Root)

if (module.hot) {
  module.hot.accept('./containers/Root', () => {
    render(Root)
  })
}

Root.js

import { hot } from 'react-hot-loader/root'

export default hot(connect(
  mapStateToProps,
  mapDispatchToProps
)(Root))

babel.config.js

  plugins: [
    'react-hot-loader/babel',
    ...,
  ],

webpack.config.js

  resolve: {
    alias: {
      'react-dom': '@hot-loader/react-dom',
    },
  },
  devServer: {
    hot: true
  }

This config emits this error twice in my console:

webpack-internal:///./node_modules/react-hot-loader/index.js:7 React-Hot-Loader: Hot Module Replacement is not enabled

if I remove 'react-hot-loader/babel from the babel config file then I only see the error once and HMR still works.

Your documentation does not mention needing to add devServer: { hot: true } in the webpack config but if I remove this I can not get HMR to work at all regardless of what combination of the other config options I use.

Adding import 'react-hot-loader' at the top of index.js changes nothing.

Removing if (module.hot) { ... from the bottom of index.js breaks HMR yet this is not part of the recommended config.

Changing export default hot(connect(mapStateToProps, mapDispatchToProps)(Root)) to export default connect(mapStateToProps, mapDispatchToProps)(hot(Root)) makes no difference

So what I can conclude is that in my setup at least, the recommended config does not work and in order to get a setup that does work (but that emits this error message stating that HMR is not loaded) I have to add additional config that is not mentioned in your documentation.

theKashey commented 5 years ago

as per the title of this issue. I see the error :"React-Hot-Loader: Hot Module Replacement is not enabled"

Look like you are talking about another issue

even though HMR does seem to be working

But according to the https://github.com/gaearon/react-hot-loader/blob/master/index.js#L10 - React-Hot-Loader is not.

Place a breakpoint here, and double check what's inside module variable. Why hot does exists for index.js, but not for react-hot-loader.

My app is an electron app and it's using webpack.

That's important difference. Electron has it's own "runtime".

module.hot.accept('./containers/Root', () => {

Then don't use react-hot-loader/root - they are doing the same

import { hot } from 'react-hot-loader/root'

Then don't use module.hot.accept - they are doing the same

Your documentation does not mention needing to add devServer: { hot: true } in the webpack config

Each setup has it's own way to properly setup HMR, and that's not quite our business. For "webpack" applications it's just --hot for webpack-dev-server, while for a custom webpack configurations it could be something very complicated.

mrfelton commented 5 years ago

For "webpack" applications it's just --hot for webpack-dev-server

What do you mean by this? Are you saying that if we are using webpack-dev-server (which we are) then the only thing we need to do is to add --hot and we can remove all of the other config??

theKashey commented 5 years ago

Usually that's all you need. No need of HotModuleReplacementPlugin, no need of additional settings - just ask webpack to be hot. Check almost any examples in our examples folder.

mrfelton commented 5 years ago

module.hot.accept('./containers/Root', () => { ... Then don't use react-hot-loader/root - they are doing the same

Ok, understood. After removing the usage of react-hot-loader/root and using only module.hot.accept it still works

import { hot } from 'react-hot-loader/root' ...Then don't use module.hot.accept - they are doing the same

Ok, understood. But in this case if I only use react-hot-loader/root without module.hot.accept it does not work.

Check almost any examples in our examples folder.

I can't see any example where the only config is to set --hot.

theKashey commented 5 years ago

https://github.com/gaearon/react-hot-loader/blob/master/examples/styled-components/package.json#L6 -> "start": "NODE_ENV=development webpack-dev-server --hot",

But in this case if I only use react-hot-loader/root without module.hot.accept it does not work.

That's strange. It should not be that way.

mrfelton commented 5 years ago

I can't see any example where the only config is to set --hot. ... -> "start": "NODE_ENV=development webpack-dev-server --hot",

Right, but --hot is not the only config!

I asked:

What do you mean by this? Are you saying that if we are using webpack-dev-server (which we are) then the only thing we need to do is to add --hot and we can remove all of the other config??

To which you replied

Usually that's all you need. No need of HotModuleReplacementPlugin, no need of additional settings - just ask webpack to be hot. Check almost any examples in our examples folder.

But in that example you pointed to you also have webpack config, you have babel config, you are wrapping the root component with hot()

... --hot is far from the only config!

theKashey commented 5 years ago

Please distinguish HMR, which is provided by the bundler, and RHL which is separate. You need:

Your issue is about "HMR is NOT properly enabled", which makes everything else senseless.

mrfelton commented 5 years ago

That helps a lot, thanks.

So as I understand it now HMR is responsible for making module.hot available and in my case at the time when RHL initialises it is not set for some reason hence why I get the error message. However by the time it gets to index.js module.hot is now set which is why I can get RHL working by manually setting it up using module.hot.accept

theKashey commented 5 years ago

.... No. If you are seeing that message - then RHL picked a production version, which does nothing. But - I just remember you said something about seeing TWO messages - could you double check - it shall be only one message, as long as a module could be executed only once, and it might be two RHLs present in your system.

Just double check messages origin.

mrfelton commented 5 years ago

There are 2 messages, both read "React-Hot-Loader: Hot Module Replacement is not enabled"

1) from webpack-internal:///./node_modules/react-hot-loader/index.js 2) from webpack://renderer/./node_modules/react-hot-loader/index.js?

mrfelton commented 5 years ago

Here are the startup logs from the console (I added a little debugging into both your index.js file and into my index.js file):

image

theKashey commented 5 years ago

That's sounds more like two Electron threads - so might be legit, but why then module.hot in RHL index.js was displayed only once?

yzw7489757 commented 5 years ago
Warning: Could not replay rendering after an error. This is likely a bug in React. Please file an issue.
Uncaught TypeError: Cannot set property 'getCurrentStack' of undefined
theKashey commented 5 years ago

@yzw7489757 - that's not very helpful.

yzw7489757 commented 5 years ago

@theKashey When I do this

// webpack.config.dev.js
alias:{
    'react-dom':'@hot-loader/react-dom'
}

Writing like this will result in an error, and commenting will warn,react-🔥-dom patch is not detected. React 16.6+ features may not work.

// App.js
import React from 'react';
import ReactDOM from 'react-dom'; //---
import './index.css';
import zhCN from 'antd/es/locale-provider/zh_CN';
import { LocaleProvider } from 'antd';
import moment from 'moment';
import App from '@/views/App'; // App as hot(App)
import 'moment/locale/zh-cn';

moment.locale('zh-cn');
ReactDOM.render(
  <LocaleProvider locale={zhCN}>
    <App />
  </LocaleProvider>,
  document.getElementById('root')
);

The following error was thrown

Warning: Could not replay rendering after an error. This is likely a bug in React. Please file an issue.
Uncaught TypeError: Cannot set property 'getCurrentStack' of undefined
// package.json
  "devDependencies": {
    "@babel/core": "^7.5.5",
    "@babel/plugin-proposal-class-properties": "^7.5.5",
    "@babel/preset-env": "^7.5.5",
    "@babel/preset-react": "^7.0.0",
    "@hot-loader/react-dom":"^16.8.6",
    "autodll-webpack-plugin": "^0.4.2",
    "babel-eslint": "^10.0.2",
    "babel-loader": "^8.0.6",
    "babel-plugin-import": "^1.12.0",
    "cache-loader": "^4.1.0",
    "css-loader": "^3.1.0",
    "react-hot-loader": "^4.12.9"
  },
  "dependencies": {
    "antd": "^3.20.5",
    "axios": "^0.19.0",
    "connected-react-router": "^6.5.2",
    "immutable": "^4.0.0-rc.12",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-redux": "^7.1.0",
    "react-router": "^5.0.1",
    "react-router-dom": "^5.0.1",
    "react-transition-group": "^4.2.1",
    "redux": "^4.0.4"
  }
}
//.babelrc
{
  "presets": ["@babel/env", "@babel/react"],
  "plugins": ["react-hot-loader/babel","@babel/plugin-proposal-class-properties"]
}

Chrome Version 75.0.3770.142 (Official Build) (64-bit) Mac Version 10.14.5 (18F132)

What should I do?

theKashey commented 5 years ago

Plase, create a separate issue with an example or a detailed stack trace. This is not the bug with React or React-Hot-Loader - I think this is dependency resolution level problem - react-dom could not read a variable react, like https://github.com/facebook/react/issues/13991

JapinderSandhu commented 5 years ago

@mrfelton

Hello all, I had this same error "React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work. #1227"

I took @mrfelton 's advice and I got rid of the error in the console, however the hot-module reloading is still not working. As was suggested I modified my webpack.dev-client.config file.

The hot module re-loading seems to work for my html sometimes however it absolutely does not work for my css.

When I save my css file it detects there is a change and the console status is `

[HMR] bundle rebuilding client.js:242 [HMR] bundle rebuilt in 80ms process-update.js:41 [HMR] Checking for updates on the server... process-update.js:112 [HMR] Nothing hot updated. process-update.js:121 [HMR] App is up to date. `

My app is a react app and it's using webpack.

Below are the key parts of my setup and if you want to try it your self please do It would be greatly appreciated as I have been stuck on this for some time and it is agonizing; In Terminal:

  1. git clone https://github.com/JapinderSandhu/noenglish_website/tree/ssr-dynamic-imports
  2. npm install
  3. npm run dev OR npm run prod

package.json

  "dependencies": {
    "@hot-loader/react-dom": "16.8.6",
    "react": "16.8.6",
    "react-dom": "16.8.6",
    "react-hot-loader": "4.8.8",
}

app.js

import React from "react"
import ReactDOM from "react-dom"
import AppRoot from "./components/AppRoot"
import { AppContainer } from "react-hot-loader"

function render(Component) {
 ReactDOM.hydrate(
   <AppContainer>
     <Component />
   </AppContainer>,
   document.getElementById("react-root")
 )
}
render(AppRoot)

if (module.hot) {
 module.hot.accept("./components/AppRoot.js", () => {
   const NewAppRoot = require("./components/AppRoot.js").default
   render(NewAppRoot)
 })
}

.babelrc

  {
"presets": [
   [
     "env",
     {
       "targets": {
         "browsers": ["last 2 versions"]
       },
       "debug": false
     }
   ],
   "babel-preset-react"
 ],
 "plugins": ["syntax-dynamic-import", "universal-import"],
 "env": {
   "development": {
     "plugins": ["react-hot-loader/babel"]
   }
 }
}

webpack.dev-client.js

const path = require("path")
const webpack = require("webpack")
const ExtractCssChunks = require("extract-css-chunks-webpack-plugin")

module.exports = {
name: "client",
 mode: "development",
 entry: {
   vendor: ["react", "react-dom"],
   main: [
     "react-hot-loader/patch",
     "babel-runtime/regenerator",
     "webpack-hot-middleware/client?reload=true",
     "./src/main.js"
   ]
 },
 resolve: {
   alias: {
     'react-dom': '@hot-loader/react-dom'
   }
 },
 output: {
   filename: "[name]-bundle.js",
   chunkFilename: "[name].js",
   path: path.resolve(__dirname, "../dist"),
   publicPath: "/"
 },
 devServer: {
   contentBase: "dist",
   overlay: true,
   hot: true,
   stats: {
     colors: true
   }
 },
 optimization: {
   runtimeChunk: {
     name: "bootstrap"
   },
   splitChunks: {
     chunks: "initial",
     cacheGroups: {
       vendors: {
         test: /[\\/]node_modules[\\/]/,
         name: "vendor"
       }
     }
   }
 },
 devtool: "source-map",
 module: {
   rules: [
     {
       test: /\.js$/,
       exclude: /node_modules/,
       use: [
         {
           loader: "babel-loader"
         }
       ]
     },
     {
       test: /\.css$/,
       use: [
         { loader: ExtractCssChunks.loader },
         {
           loader: "css-loader"
         }
       ]
     },
     {
       test: /\.(jpg|png|gif)$/,
       use: [
         {
           loader: "file-loader",
           options: {
             name: "assets/page1_media/images/[name].[ext]"
           }
         }
       ]
     },
     {
       test: /\.md$/,
       use: [
         {
           loader: "markdown-with-front-matter-loader"
         }
       ]
     }
   ]
 },
 plugins: [
   new ExtractCssChunks({ hot: true }),
   new webpack.DefinePlugin({
     "process.env": {
       NODE_ENV: JSON.stringify("development"),
       WEBPACK: true
     }
   }),
   new webpack.HotModuleReplacementPlugin()
 ]
}
klausbadelt commented 5 years ago

I can confirm @rybon's https://github.com/gaearon/react-hot-loader/issues/1227#issuecomment-482518698 is working 🔥 - and the easiest to implement.

In case it helps anyone, here's how this looks like for Rails Webpacker:

// package.json

{
  "dependencies": {
    "react-hot-loader": "^4.8.3"
    // ...
  }
  // ...
}
# config/webpacker.yml

# ...
  dev_server:
    https: false
    host: localhost
    port: 3035
    public: localhost:3035
    hmr: true
    # Inline should be set to true if using HMR
    inline: true
    overlay: true
    compress: true
    disable_host_check: true
    use_local_ip: false
    quiet: false
    headers:
      'Access-Control-Allow-Origin': '*'
    watch_options:
      ignored: '**/node_modules/**'
    client_log_level: trace

# ...

// config/webpack/environment.js

const { environment } = require('@rails/webpacker')
// ..
const reactHotReload = {
  test: /\.(js|jsx)$/,
  use: 'react-hot-loader/webpack',
  include: /node_modules/,
}
environment.loaders.insert('reactHotReload', reactHotReload, { after: 'babel'})

module.exports = environment

and then the following pattern in your code (Note: to keep state with redux & Co, don't apply this to top level App but to lower level components (containers, pages etc)):

import { hot } from 'react-hot-loader/root'
import React from 'react'
// ...
export default hot(MyComponent)

As @rybon says, no need for @hot-loader/react-dom and webpack alias ReactDOM patching setup.

lili21 commented 5 years ago

Doesn't work If I'm using webpack externals.

// webpack.config.js
...
externals: {
  'react-dom': 'ReactDOM'
}
...
theKashey commented 5 years ago

Don't use them in a dev mode. That's the only advice I could give you, sorry.

lili21 commented 5 years ago

I tried removing the externals, tried config webpack loader as @rybon said, tried following the guide from README. none work like expect. it works in a weird way. the hot reload always one step behind. I change the code from A to B, it doesn't work, the content still is A in the browser. change the code again, from B to C, the content changed to B in the browser.