gaearon / react-hot-loader

Tweak React components in real time. (Deprecated: use Fast Refresh instead.)

Home Page:http://gaearon.github.io/react-hot-loader/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Breaks if adding/removing/replacing hooks

TotooriaHyperion opened this issue · comments

If you are reporting a bug or having an issue setting up React Hot Loader, please fill in below. For feature requests, feel free to remove this template entirely.

Description

What you are reporting:

import React, { useEffect, useState } from "react";
import { hot } from "react-hot-loader/root";

export default hot(
  React.memo(function App() {
    // + const [_0] = useState(0);
    useEffect(() => {
      console.log("app first render");
      return () => {
        console.log("app will unmount");
      };
    }, []);
    useEffect(() => {
      console.log("app updated");
      return () => {
        console.log("app before update");
      };
    });
    return <>App</>;
  }),
);

simply add a useState, then hmr breaks.

Expected behavior

What you think should happen:
Destory the old Component with 2 useEffect, and render a new Component with useState + 2 useEffect.

Actual behavior

What actually happens:
Throws error:

Uncaught Invariant Violation: Should have a queue. This is likely a bug in React. Please file an issue.

Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://fb.me/rules-of-hooks

Previous render Next render

  1. useEffect useState
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    in App (created by HotExportedComponent)
    in AppContainer (created by HotExportedComponent)
    in HotExportedComponent

Environment

React Hot Loader version:
"@hot-loader/react-dom": "^16.10.2",
"react-hot-loader": "^4.12.15",

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

  1. node -v:v10.16.0
  2. npm -v:6.12.0

Then, specify:

  1. Operating system:Mac OS 10.14.5
  2. Browser and version:Chrome 77.0.3865.90

Reproducible Demo

Please take the time to create a new project that reproduces the issue.

You can copy your project that experiences the problem and start removing things until you’re left with the minimal reproducible demo. This helps contributors, and you might get to the root of your problem during that process.

Push to GitHub and paste the link here.

Strange. That should work...

When I comment out the useState, everything goes fine without reload the page.

I think changing effect should cause Component to change, then react's diff algorithm should have known this component has changed, and will destroy the old one and render the new one.

I also find that when I save file first time, I got that error, but I save it again without any change, it renders the new component.

This is my entry:

const main = [
    `webpack-hot-middleware/client?path=${publicPath}webpack_hot&timeout=5000&overlay=false&reload=true`,
    "webpack/hot/only-dev-server",
    `${srcPath}/index.tsx`,
  ]

This is my middleware

  const compiler = webpack(config);
  const server = express();
  server.use(
    DevMiddleware(compiler, {
      publicPath: config.output!.publicPath!,
    }),
  );
  server.use(
    HotMiddleware(compiler, {
      path: `/${proxied_path}/webpack_hot`,
      heartbeat: 1000,
    }),
  );

Try to hoist component, there might an error with it, as long as it's not a top level variable.

function App() {
    // + const [_0] = useState(0);
    useEffect(() => {
      console.log("app first render");
      return () => {
        console.log("app will unmount");
      };
    }, []);
    useEffect(() => {
      console.log("app updated");
      return () => {
        console.log("app before update");
      };
    });
    return <>App</>;
  });

export default hot(React.memo(App));

wait a moment... hook reloading is relaying on babel plugin. Without it, they would behave exactly as in your example.
You have to use babel, however it might apply only RHL plugin, keeping TS transformation for ts-loader

image

looks like it triggers unmount effect, but still throw the error.

emmm okey. I don't read the doc carefully to know I have to use babel...
This issue can be closed.