Skip to content

Commit

Permalink
fix(dev): Wait for the router to be initialized in RemixBrowser befor…
Browse files Browse the repository at this point in the history
…e applying HMR changes (#6767)

Co-authored-by: Jacob Ebey <jacob.ebey@live.com>
Co-authored-by: Pedro Cattori <pcattori@gmail.com>
  • Loading branch information
3 people authored Jul 11, 2023
1 parent 79f9ff3 commit afb5413
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/mean-months-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": patch
---

fix router race condition for hmr
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -556,4 +556,5 @@
- tanerijun
- toufiqnuur
- ally1002
- defjosiah
- AlemTuzlak
29 changes: 28 additions & 1 deletion packages/remix-react/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand Down Expand Up @@ -180,7 +202,7 @@ export function RemixBrowser(_props: RemixBrowserProps): ReactElement {
window.__remixContext.future.v2_normalizeFormMethod,
},
});

// 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
Expand All @@ -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);
Expand Down

0 comments on commit afb5413

Please sign in to comment.