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

Typescript react component force-reloading entire page for trivial change

krainboltgreene opened this issue · comments

When I make a trivial change (1 to 2) the hot reloader sees the change and reloads the page, instead of doing a patch. This is the message I see before the reload:

react_devtools_backend.js:2430 [HMR] Error: Aborted because ./lib/client/elements/Page/index.tsx is not accepted
Update propagation: ./lib/client/elements/Page/index.tsx -> ./lib/client/elements/index.ts -> ./lib/client/index.tsx -> 0
    at hotApplyInternal (http://localhost:8080/browser-client.js?71fa7ddc8097054cc323:508:30)
    at hotApply (http://localhost:8080/browser-client.js?71fa7ddc8097054cc323:362:19)
    at http://localhost:8080/browser-client.js?71fa7ddc8097054cc323:337:22
overrideMethod @ react_devtools_backend.js:2430
./node_modules/webpack/hot/log.js.module.exports @ browser-client.js:218338
(anonymous) @ browser-client.js:218217
Promise.catch (async)
check @ browser-client.js:218210
(anonymous) @ browser-client.js:218229
emit @ browser-client.js:141324
reloadApp @ browser-client.js:217947
warnings @ browser-client.js:217496
(anonymous) @ browser-client.js:217715
sock.onmessage @ browser-client.js:217327
EventTarget.dispatchEvent @ browser-client.js:208639
(anonymous) @ browser-client.js:209357
SockJS._transportMessage @ browser-client.js:209355
EventEmitter.emit @ browser-client.js:208555
WebSocketTransport.ws.onmessage @ browser-client.js:211431

Versions:

  • react ^16.14.0
  • react-hot-loader ^4.13.0
  • @hot-loader/react-dom ^16.14.0
  • node 14.1.0
  • npm 6.14.8
  • macos 10.15.7
  • chrome 87.0.4280.67

Demo application:

https://github.com/krainboltgreene/blank-application

All correct:

  • the update went like ./lib/client/elements/Page/index.tsx -> ./lib/client/elements/index.ts -> ./lib/client/index.tsx -> 0
  • hot boundary is installed only on lib/client/elements/Application/index.tsx

👉 just import "react-hot-loader/root"; at ./lib/client/elements/index.ts to establish one more boundary and that's all.

Webpack compiled correctly, but when I load the page I get an immediate hot reloading issue:

Screen Shot 2020-11-24 at 9 40 38 PM

Screen Shot 2020-11-24 at 9 41 02 PM

Any thoughts? I didn't have this issue before typescript, I believe.

Okay, so I figured out why it "used to work", turns out I can modify some components and get the expected hot reloading. I think the way I'm stacking my components is causing issues.

So at the root level I have:

render(
  <BrowserRouter>
    <Application />
  </BrowserRouter>,
  document.querySelector("#application")
);

And Application looks like this:

export default hot(function Application (): JSX.Element {
  return <ErrorBoundary>
    <Router />
  </ErrorBoundary>;
});

With Router looking like this:

export default function Router (): JSX.Element {
  return <Switch>
    <Route exact path="/" component={LandingPage} />
  </Switch>;
}

Finally, LandingPage looks like:

export default function LandingPage (): JSX.Element {
  return <Page as="LandingPage">
    <h1>2</h1>
  </Page>;
}

So amusingly, when I change LandingPage I get the correct hot reloading result. When I change Page I get the unexpected hot reloading.

I see now that anything inside the chain

pages/LandingPage/index.tsx -> pages/index.ts -> elements/Router/index.tsx -> elements/Application/index.tsx

gets hot reloaded correctly, anything that is "outside" that chain does not. I'm still trying to figure out why a change, to say elements/Link looks like

Update propagation: ./lib/client/elements/Link/index.tsx -> ./lib/client/elements/index.ts -> ./lib/client/index.tsx -> 0

Why does ./lib/client/elements/index.ts -> ./lib/client/index.tsx not include the steps where it hits LandingPage, pages/index.ts, Router, then Application?

Okay, I managed to get it working correctly by moving the hot() call higher in the stack:

// client/index.tsx
const HotReloadedComponent = hot(() => <BrowserRouter><Application /></BrowserRouter>);

render(
  <HotReloadedComponent />,
  document.querySelector("#application")
);

Well, having hot on the topmost level working only if you have react-dom/react referenced in any other branch.
As during HMR everything "below" hot will be updated (destroyed and recreated again) - you probably need it a little below.
The simplest way is to move all the code for HotReloadedComponent into a separate module, keeping dependencies for the top one, with render at minimum.

Well I tried moving the HotReloadedComponent into Application and hit the same problem. I'll give making HotReloadedComponent into it's own thing a try.