Fix Router navigation state handling

The changes improve state handling during client-side navigation by: -
Using correct function updates with setAppState - Preserving abort
controller on updates - Adding proper cache ID comparison - Moving
router instance to constants
This commit is contained in:
Alistair Smith
2025-09-09 21:39:19 -07:00
parent 20d2f3805e
commit e50385879b
2 changed files with 11 additions and 17 deletions

View File

@@ -1,10 +1,8 @@
import { onServerSideReload } from "bun:app/client";
import { hydrateRoot } from "react-dom/client";
import { initialRscPayloadThen } from "./client/app.ts";
import { router } from "./client/constants.ts";
import { Root } from "./client/root.tsx";
import { Router } from "./client/router.ts";
const router = new Router();
hydrateRoot(document, <Root />, {
onUncaughtError(e) {
@@ -50,13 +48,8 @@ const firstPageId = Date.now();
}
}
// Client side navigation is implemented by updating the app's `useState` with a
// new RSC payload promise. Callers of `navigate` are expected to manage history
// state. A navigation id is used
window.addEventListener("popstate", async event => {
const state = typeof event.state === "number" ? event.state : undefined;
await router.navigate(location.href, state);
});

View File

@@ -109,7 +109,7 @@ export class Router {
// Save this promise so that pressing the back button in the browser navigates
// to the same instance of the old page, instead of re-fetching it.
if (cacheId) {
if (cacheId !== undefined) {
this.cachedPages.set(cacheId, {
css: [...this.css.getList()],
element: p,
@@ -124,21 +124,22 @@ export class Router {
}
// Tell react about the new page promise
if (document.startViewTransition as unknown) {
if (document.startViewTransition) {
document.startViewTransition(() => {
flushSync(() => {
if (thisNavigationId === this.lastNavigationId)
setAppState({
if (thisNavigationId === this.lastNavigationId) {
setAppState(old => ({
rsc: p,
abortOnRender: olderController ?? undefined,
});
abortOnRender: olderController ?? old.abortOnRender,
}));
}
});
});
} else {
setAppState({
setAppState(old => ({
rsc: p,
abortOnRender: olderController ?? undefined,
});
abortOnRender: olderController ?? old.abortOnRender,
}));
}
}
}