-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
fix(dev): Wait for the router to be initialized in RemixBrowser before applying HMR changes #6767
Merged
pcattori
merged 6 commits into
remix-run:dev
from
defjosiah:defjosiah/act/hmr-promise-router
Jul 11, 2023
Merged
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
70df423
Guard usage of router with a promise for hmr
defjosiah 005b186
Sign CLA
defjosiah aba7ca6
Add changeset
defjosiah 59552f4
- handle promise rejection - notify after reload check to avoid any e…
jacob-ebey 6076545
Merge branch 'dev' into defjosiah/act/hmr-promise-router
pcattori fe8175d
Merge branch 'dev' into defjosiah/act/hmr-promise-router
jacob-ebey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@remix-run/react": patch | ||
--- | ||
|
||
fix router race condition for hmr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -555,3 +555,4 @@ | |
- mrkhosravian | ||
- tanerijun | ||
- ally1002 | ||
- defjosiah |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,19 @@ declare global { | |
|
||
let router: Router; | ||
let hmrAbortController: AbortController | undefined; | ||
let hmrRouterReadyResolve: ((router: Router) => void) | undefined; | ||
// There's a race condition with HMR where the remix:manifest is signaled before | ||
// the router is assigned in the RemixBrowser component. This promise gates the | ||
// HMR handler until the router is ready | ||
let hmrRouterReadyPromise = new Promise<Router>((resolve) => { | ||
// body of a promise is executed immediately, so this can be resolved outside | ||
// of the promise body | ||
hmrRouterReadyResolve = resolve; | ||
}).catch(() => { | ||
// This is a noop catch handler to avoid unhandled promise rejection warnings | ||
// in the console. The promise is never rejected. | ||
return undefined; | ||
}); | ||
|
||
if (import.meta && import.meta.hot) { | ||
import.meta.hot.accept( | ||
|
@@ -59,6 +72,15 @@ if (import.meta && import.meta.hot) { | |
assetsManifest: EntryContext["manifest"]; | ||
needsRevalidation: Set<string>; | ||
}) => { | ||
let router = await hmrRouterReadyPromise; | ||
// This should never happen, but just in case... | ||
if (!router) { | ||
console.error( | ||
"Failed to accept HMR update because the router was not ready." | ||
); | ||
return; | ||
} | ||
|
||
let routeIds = [ | ||
...new Set( | ||
router.state.matches | ||
|
@@ -180,7 +202,7 @@ export function RemixBrowser(_props: RemixBrowserProps): ReactElement { | |
window.__remixContext.future.v2_normalizeFormMethod, | ||
}, | ||
}); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jacob-ebey this looks like unintentional whitespace |
||
// Hard reload if the path we tried to load is not the current path. | ||
// This is usually the result of 2 rapid back/forward clicks from an | ||
// external site into a Remix app, where we initially start the load for | ||
|
@@ -197,6 +219,11 @@ export function RemixBrowser(_props: RemixBrowserProps): ReactElement { | |
console.error(errorMsg); | ||
window.location.reload(); | ||
} | ||
|
||
// Notify that the router is ready for HMR | ||
if (hmrRouterReadyResolve) { | ||
hmrRouterReadyResolve(router); | ||
} | ||
} | ||
|
||
let [location, setLocation] = React.useState(router.state.location); | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
You could use a
Channel
here instead so you don't have to manually setresolve
and check for it later to make TS happy: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.
That's fine with me, however, it looks like that Channel implementation is in remix-dev package?
remix/packages/remix-dev/channel.ts
Line 28 in 0d22ee9
And it doesn't look like there's an import in this react package, so not sure how y'all would want to do that.
Also, typescript seems happy with it for me! I'm getting all the proper types out of this.
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.
Oh yea good point. Not worth introducing the channel abstraction just for this if its not already in this package.
For TS, I just mean that
if (hmrRouterReadyResolve) {
is unnecessary with channel, but needed here since TS doesn't know thathmrRouterReadyResolve
is guaranteed to be set.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.
Ahh, got it!
Yeah, I really like that Channel abstraction, probably going to shamelessly use it somewhere else 😂