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 for folder outside root #1400

Open neberej opened 4 years ago

neberej commented 4 years ago

Description

I can't get react-hot-loader to work for dependencies outside of root project.

Expected behavior

React-hot-loader should work as intended.

Actual behavior

Browser refreshes the complete page

Environment

React Hot Loader version: 8

Run these commands in the project folder and fill in their results:

  1. node -v: 10.16.3
  2. npm -v: 6.9.0

Then, specify:

  1. Operating system: Both Windows and Mac
  2. Browser and version: Chrome

Details

I have two projects (on same level)

[app] [service]

Inside app/webpack.conf.js, i have defined alias as:

resolve: {
      alias: {
        '@src': path.resolve(__dirname, 'src),
        '@service': path.resolve(__dirname, '../service)
      }
    }

My entry file (Home.js):

import Content from '@src/content/Content';
import Service from '@service/service/Service';

...

export default hot(Home);

Now hot loader works for Content but not for Service.

Is there anything I am missing here?

This is what I see on console.

Ignored an update to unaccepted module ../service/service/Service.js ->....
[HMR] The following modules couldn't be hot updated: (Full reload needed)
[HMR] - ../service/service/Service.js
[HMR] Reloading page
theKashey commented 4 years ago

Ignored an update to unaccepted module ../service/service/Service.js ->....

What is ... why it did not accept the module update? That's webpack and HMR confuguration, not React-Hot-Loader

neberej commented 4 years ago

I tried to isolate the issue. When I change something on a file that is outside root folder, I see:

[HMR] bundle rebuilding
client.js?f33d:250 [HMR] bundle rebuilt in 1165ms
process-update.js?e135:51 [HMR] Checking for updates on the server...
process-update.js?e135:125 [HMR] Updated modules:
process-update.js?e135:125 [HMR] - ../service/service/Service.js
process-update.js?e135:125 [HMR] - ../service/index.js
process-update.js?e135:125 [HMR] - ./src/Containers/App.js
...other files...
process-update.js?e135:125 [HMR] App is up to date.

Page doesn't update. When I refresh the page, I see the update.

theKashey commented 4 years ago

It might be due to the absence of a babel plugin for them. There is no simple way to fix it, except enforce babel configuration for babel-loader, but this would not work for everyone.

neberej commented 4 years ago

This is my babel config.

 module: {
        rules: [{
                test: /\.js$/,
                exclude: /node_modules/,
               include: [
                   path.resolve(__dirname, "src"),
                   path.resolve(__dirname, "../service)
                ],
                loader: 'babel-loader'
            },
            ....

Everything works great except the hot-loader part.

except enforce babel configuration for babel-loader

How would I do this?

theKashey commented 4 years ago

Well, you are doing it already. Just add babel options to the loader, not let it use .babelrc - https://github.com/babel/babel-loader#options

neberej commented 4 years ago

I have a babel.config.js file already.

module.exports = {
  presets: ["@babel/preset-react", ["@babel/preset-env", {"modules": "commonjs"}]],
  plugins: [
    "react-hot-loader/babel",
    ["module-resolver", {
        "root": ["./src],
        "alias": {
          "@service": "../service"
        }
      }
    }]
  ]
};

Everything works except hot loader in the service folder which is on same level as app.

theKashey commented 4 years ago

One more thought - it might use "the other" react-hot-loader. Try to "pin" the only one using webpack aliases, like you probably already did with React itself.

neberej commented 4 years ago

I got semi-working.

On a folder outside root, I have

module.exports = {
  Gallery,
  Overlay,
  Accordion,
  ....
}

If I go to individual components and apply hot-loader, it works. Gallery.js

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

Is there a way to apply it to all module.exports rather than add it to each component individually? Also, I have imported root on main project. Any drawback on importing root again on another folder?

I tried adding just babel.config.js, importing just 'react-hot-loader' via alias but none of them seem to work.

theKashey commented 4 years ago

So roughly - react-hot-loader is working, but react-hot-loader/babel might be not. And the "original" reason was hidden inside webpack hot-module-update "bubbling" - the updates reaches one of the "ends" not being accepted, and webpack reloads the page.

To be honest I am not sure why it's not working. The Devil is hidden in details.