Skip to content

Commit

Permalink
Fix HDR for single fetch (#9954)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 authored Sep 6, 2024
1 parent 1a97d25 commit 5900332
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/rude-cows-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@remix-run/react": patch
---

[REMOVE] Fix HDR for single fetch
56 changes: 56 additions & 0 deletions integration/vite-hmr-hdr-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
EXPRESS_SERVER,
viteConfig,
} from "./helpers/vite.js";
import { js } from "./helpers/create-fixture.js";

const indexRoute = `
// imports
Expand Down Expand Up @@ -112,6 +113,61 @@ test("Vite / HMR & HDR / mdx", async ({ page, viteDev }) => {
expect(page.errors).toEqual([]);
});

test.describe("single fetch", () => {
test("Vite / HMR & HDR / vite dev", async ({
page,
browserName,
viteDev,
}) => {
let files: Files = async ({ port }) => ({
"vite.config.js": js`
import { vitePlugin as remix } from "@remix-run/dev";
export default {
${await viteConfig.server({ port })}
plugins: [
remix({
future: {
unstable_singleFetch: true
},
})
]
}
`,
"app/routes/_index.tsx": indexRoute,
});
let { cwd, port } = await viteDev(files);
await workflow({ page, browserName, cwd, port });
});

test("Vite / HMR & HDR / express", async ({
page,
browserName,
customDev,
}) => {
let files: Files = async ({ port }) => ({
"vite.config.js": js`
import { vitePlugin as remix } from "@remix-run/dev";
export default {
${await viteConfig.server({ port })}
plugins: [
remix({
future: {
unstable_singleFetch: true
},
})
]
}
`,
"server.mjs": EXPRESS_SERVER({ port }),
"app/routes/_index.tsx": indexRoute,
});
let { cwd, port } = await customDev(files);
await workflow({ page, browserName, cwd, port });
});
});

async function workflow({
page,
browserName,
Expand Down
8 changes: 7 additions & 1 deletion packages/remix-dev/vite/static/refresh-utils.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ const enqueueUpdate = debounce(async () => {
window.__remixRouteModuleUpdates.clear();
}

await revalidate();
try {
window.__remixHdrActive = true;
await revalidate();
} finally {
window.__remixHdrActive = false;
}

if (manifest) {
Object.assign(window.__remixManifest, manifest);
}
Expand Down
1 change: 1 addition & 0 deletions packages/remix-react/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ declare global {
var __remixRouteModules: RouteModules;
var __remixManifest: AssetsManifest;
var __remixRevalidation: number | undefined;
var __remixHdrActive: boolean;
var __remixClearCriticalCss: (() => void) | undefined;
var $RefreshRuntime$: {
performReactRefresh: () => void;
Expand Down
55 changes: 28 additions & 27 deletions packages/remix-react/single-fetch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -261,46 +261,47 @@ async function singleFetchLoaderNavigationStrategy(
results[m.route.id] = { type: "error", result: e };
}
return;
} else if (!manifest.routes[m.route.id].hasLoader) {
// If we don't have a server loader, then we don't care about the HTTP
// call and can just send back a `null` - because we _do_ have a `loader`
// in the client router handling route module/styles loads
results[m.route.id] = {
type: "data",
result: null,
};
return;
}

// Otherwise, we want to load this route on the server and can lump this
// it in with the others on a singular promise
routesParams.add(m.route.id);
// Load this route on the server if it has a loader
if (manifest.routes[m.route.id].hasLoader) {
routesParams.add(m.route.id);
}

await handler(async () => {
try {
// Lump this match in with the others on a singular promise
try {
let result = await handler(async () => {
let data = await singleFetchDfd.promise;
results[m.route.id] = {
type: "data",
result: unwrapSingleFetchResults(data, m.route.id),
};
} catch (e) {
results[m.route.id] = {
type: "error",
result: e,
};
}
});
return unwrapSingleFetchResults(data, m.route.id);
});
results[m.route.id] = {
type: "data",
result,
};
} catch (e) {
results[m.route.id] = {
type: "error",
result: e,
};
}
})
)
);

// Wait for all routes to resolve above before we make the HTTP call
await routesLoadedPromise;

// Don't make any single fetch server calls:
// We can skip the server call:
// - On initial hydration - only clientLoaders can pass through via `clientLoader.hydrate`
// - If there are no routes to fetch from the server
if (!router.state.initialized || routesParams.size === 0) {
//
// One exception - if we are performing an HDR revalidation we have to call
// the server in case a new loader has shown up that the manifest doesn't yet
// know about
if (
(!router.state.initialized || routesParams.size === 0) &&
!window.__remixHdrActive
) {
singleFetchDfd.resolve({});
} else {
try {
Expand Down

0 comments on commit 5900332

Please sign in to comment.