-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Upgrade react-hot-loader
to 4.6
#10455
Conversation
I took the Medium post and Anton's comment to mean we wouldn't need to use |
I think we still need |
Hmm... so this is looking good. Unfortunately, when I test with my hooks-example, I'm getting
loader.getResourcesForPathname(window.location.pathname).then(() => {
let Root = hot(module)(preferDefault(require(`./root`))) // this line
domReady(() => {
renderer(<Root />, rootElement, () => {
apiRunner(`onInitialClientRender`)
})
})
}) I'll investigate a little further. As info, I'm using |
Does |
I'm not able to test it myself at the moment, but it would be cool if this helps fix hooks at the top (pages) level. (Or is that more of a Gatsby internals issue?) |
@DSchau hmm, I am getting the same error now in one of my test projects. @wKovacs64 yes it now also works at the top level (when it works at all 😅 ) |
The (partial) fix is to change how we are marking the root component as hot. In we need to change line 60 from let Root = hot(module)(preferDefault(require(`./root`))) to let Root = hot(preferDefault(require(`./root`))) See the README of react-hot-loader for more info! |
@DSchau I can also reproduce that, I thought it was related to the missing However, I did find a way to use the patch with a user-provided |
Fixed - the new I will now test it with a few more projects, but the one where the two errors occurred (https://github.com/jgierer12/gatsby-api-proposal-demo) finally works now 👍 |
@DSchau what are your thoughts about #10455 (comment)? |
@jgierer12 re:
Keep it as is for now. It's relatively tangential to the PR and it appears that none of those files have been updated for the last two years 🙃 |
Also I'm not sure we want to implement the webpack loader as we've done in 3894f0a. I think we should prefer the babel loader, which we currently use. This was working with the babel loader, and there's some spooky language about using the webpack plugin with class plugins.
|
Hm, for me it always hot reloads correctly. Is the repo you're testing this on public?
Yes, that also confused me but then again the medium article made it sound like something that's highly recommended. However, I just tried turning it off and it worked just as well. |
@jgierer12 yup! https://github.com/dschau/gatsby-react-hooks/tree/4.6.0 I have gatsby-dev CLI set up to run as part of the |
This comment has been minimized.
This comment has been minimized.
@@ -57,7 +57,7 @@ apiRunnerAsync(`onClientEntry`).then(() => { | |||
loader.addDevRequires(syncRequires) | |||
|
|||
loader.getResourcesForPathname(window.location.pathname).then(() => { | |||
let Root = hot(module)(preferDefault(require(`./root`))) | |||
let Root = preferDefault(require(`./root`)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New /root/hot
API will not stand asynchronously between the first part and the second.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that hot
should stay in this module? i.e.
let Root = hot(preferDefault(require(`./root`)))
That was the reason for the original hot update was not successful
error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hot
or import 'react-hot-loader/root
are calling module.hot.accept
for the current module, thus setting self-acceptance.
It's expected that some action would take a place on module hot update immediately and in a synchronous mode. Using hot
inside indirectly called function are breaking some expectations - "module" could not accept it, as long as the "real action" is not yet called.
Here - if you want to make ./root
hot - you have to make IT hot. And that's was done.
The second moment is about HMR and an update propagation.
When you change a file webpack bubbles update to parent, unless parent will accept the change, then webpack will update parent and everything below it.
We had WFT-level issues if "parent" is the module with react-dom/render
- everything got wiped and regenerated, making reconciliation impossible (Symbols
could be updated for example).
This is not quite clear from RHL documentation, sorry. The difference is between self-accept
in v4, and child-accept
(ie module.hot.accept('./containers/App') in v3.
Here you might want to accept only ./root
, but would accept everything.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@DSchau Could you give this another look? It now works again on my end. |
packages/gatsby/cache-dir/app.js
Outdated
@@ -19,6 +20,9 @@ setConfig({ | |||
pureRender: true, | |||
}) | |||
|
|||
const preferDefault = m => (m && m.default) || m | |||
let Root = hot(preferDefault(require(`./root`))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So - as I mentioned in another comment - hot
here would setup current module as self-accepted, wiping and updating everything below it every time.
- react-dom
- socketIo
- emitter
It would be better to move it to ./root
or have another file, imported above this one with some important imports. That would link "important" modules with another "parent branch", and protect them during hot update.
Sound recommendation - have hot
below(in module terms) react-dom/render
and redux/createStore
to maintain their values on update. You have to stop event propagation before it bubbles to the places you should keep between updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@theKashey thanks again, I now moved it into .cache/sync-requires.js
so only page components are hot loaded.
@DSchau This is not the most beautiful solution, but it was the best I could think of without applying hot
further up. An alternative could be to map over syncRequires
in app.js
and call hot
there instead of writing it into the generated sync-requires.js
.
Checking this out now! Thanks for the work on this both of you, it's appreciated! |
@@ -72,15 +72,17 @@ const writePages = async () => { | |||
components = _.uniqBy(components, c => c.componentChunkName) | |||
|
|||
// Create file with sync requires of components/json files. | |||
let syncRequires = `// prefer default export if available | |||
let syncRequires = `const { hot } = require("react-hot-loader/root") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of this looks great--and I was able to validate that this does fix a number of issues with hot reloading + hooks, namely:
- Hot reloading of page components
- Hot reloading of components in components/ directory
- Hot reloading does not show an error on launch
- Hot reloading (still) works with class components, markdown files, etc.
However -- I don't think this is the best place to implement this hot reloading functionality. react-hot-loader is basically a noop in non-development mode (e.g. NODE_ENV=development), but I don't love the idea of putting this in front of every page in every environment.
@pieh do you have a better idea as to where this hot reloading functionality should be implemented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I originally had it in app.js
(be3d53e) which also worked fine, but I moved it into the generated sync-requires.js
as per #10455 (comment).
Aren't the syncRequires
only loaded in development mode anyways and replaced by asyncRequires
in prod?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't the
syncRequires
only loaded in development mode anyways and replaced byasyncRequires
in prod?
Yes, they are currently - but we will be looking to split dev bundles similar to what we do for production - which should result in faster recompilations and also better experience for hosted/not-local preview servers. Not yet sure how would that affect hot reloads.
Taking one final look at this--but think we're good to go and merge this in. Will report back! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me; merging!
|
I think this caused a fairly large regression in See #10528 for more info. Seems to be related to anonymous arrow functions being used as page components. |
calling `hot` for a different components is making them the same. It was designed to be applied once and to a default export only.
It would work is This is one line, which should be removed from RHL side, and then you will be able to use as much |
v4.6.2 is working well on provided demo. |
@theKashey could you elaborate on what the implications are when calling I think this would be the best/only other solution without changing the underlying component structure. Edit: Sorry, just realized you've already fixed it in 4.6.2. Thanks for the quick fix! |
The core of RHL is a "proxy" - it knows that instead of OldComponent it should use NewComponent, and that is done via top level variables registration. Basically UID for a Component is And I have no idea, why it did work before. |
React hooks are not functional with these versions: Is there additional work to utilize the new React features in a new Gatsby project created by the CLI?
|
@google-mac can you share a repo? That's effectively the exact same stack I'm rolling with in this repo. I just validated that |
@DSchau I just got it working in a different test. I believe there are more requirements than what I mentioned above. I looked through your repo and updated the dependencies to match for the other plugins. So it is more than React (and React-DOM), and Gatsby. Thanks for the help! |
Fixes gatsbyjs#9489 ~~The docs also [recommend](https://github.com/gaearon/react-hot-loader#react--dom) (but don't require) using a patched `react-dom`, but as I understand it that would override the user's `react-dom` version we have as a peer dependency so I left it out.~~ ➡️ gatsbyjs@3894f0a /cc @wKovacs64
Right now There are two ways to fix it:
|
Fixes #9489
The docs also recommend (but don't require) using a patched➡️ 3894f0areact-dom
, but as I understand it that would override the user'sreact-dom
version we have as a peer dependency so I left it out./cc @wKovacs64