Skip to content

Commit

Permalink
Rename patchRoutesOnMiss to patchRoutesOnNavigation (#11888)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed Aug 14, 2024
1 parent bd4ac8e commit 5a6545b
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 101 deletions.
7 changes: 7 additions & 0 deletions .changeset/silver-coats-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"react-router-dom": patch
"react-router": patch
"@remix-run/router": patch
---

Rename `unstable_patchRoutesOnMiss` to `unstable_patchRoutesOnNavigation` because it will now be called on the first navigation to paths matching splat/param routes in case there exists a higher-scoring route match not yet discovered
34 changes: 23 additions & 11 deletions docs/routers/create-browser-router.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function createBrowserRouter(
future?: FutureConfig;
hydrationData?: HydrationState;
unstable_dataStrategy?: unstable_DataStrategyFunction;
unstable_patchRoutesOnMiss?: unstable_PatchRoutesOnMissFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
window?: Window;
}
): RemixRouter;
Expand Down Expand Up @@ -384,7 +384,7 @@ let router = createBrowserRouter(routes, {
});
```

## `opts.unstable_patchRoutesOnMiss`
## `opts.unstable_patchRoutesOnNavigation`

<docs-warning>This API is marked "unstable" so it is subject to breaking API changes in minor releases</docs-warning>

Expand All @@ -394,12 +394,12 @@ To combat this, we introduced [`route.lazy`][route-lazy] in [v6.9.0][6-9-0] whic

In some cases, even this doesn't go far enough. For very large applications, providing all route definitions up front can be prohibitively expensive. Additionally, it might not even be possible to provide all route definitions up front in certain Micro-Frontend or Module-Federation architectures.

This is where `unstable_patchRoutesOnMiss` comes in ([RFC][fog-of-war-rfc]). This API is for advanced use-cases where you are unable to provide the full route tree up-front and need a way to lazily "discover" portions of the route tree at runtime. This feature is often referred to as ["Fog of War"][fog-of-war] because similar to how video games expand the "world" as you move around - the router would be expanding its routing tree as the user navigated around the app - but would only ever end up loading portions of the tree that the user visited.
This is where `unstable_patchRoutesOnNavigation` comes in ([RFC][fog-of-war-rfc]). This API is for advanced use-cases where you are unable to provide the full route tree up-front and need a way to lazily "discover" portions of the route tree at runtime. This feature is often referred to as ["Fog of War"][fog-of-war] because similar to how video games expand the "world" as you move around - the router would be expanding its routing tree as the user navigated around the app - but would only ever end up loading portions of the tree that the user visited.

### Type Declaration

```ts
export interface unstable_PatchRoutesOnMissFunction {
export interface unstable_PatchRoutesOnNavigationFunction {
(opts: {
path: string;
matches: RouteMatch[];
Expand All @@ -413,7 +413,7 @@ export interface unstable_PatchRoutesOnMissFunction {

### Overview

`unstable_patchRoutesOnMiss` will be called anytime React Router is unable to match a `path`. The arguments include the `path`, any partial `matches`, and a `patch` function you can call to patch new routes into the tree at a specific location. This method is executed during the `loading` portion of the navigation for `GET` requests and during the `submitting` portion of the navigation for non-`GET` requests.
`unstable_patchRoutesOnNavigation` will be called anytime React Router is unable to match a `path`. The arguments include the `path`, any partial `matches`, and a `patch` function you can call to patch new routes into the tree at a specific location. This method is executed during the `loading` portion of the navigation for `GET` requests and during the `submitting` portion of the navigation for non-`GET` requests.

**Patching children into an existing route**

Expand All @@ -427,7 +427,10 @@ const router = createBrowserRouter(
},
],
{
async unstable_patchRoutesOnMiss({ path, patch }) {
async unstable_patchRoutesOnNavigation({
path,
patch,
}) {
if (path === "/a") {
// Load/patch the `a` route as a child of the route with id `root`
let route = await getARoute();
Expand All @@ -439,7 +442,7 @@ const router = createBrowserRouter(
);
```

In the above example, if the user clicks a link to `/a`, React Router won't be able to match it initially and will call `patchRoutesOnMiss` with `/a` and a `matches` array containing the root route match. By calling `patch`, the `a` route will be added to the route tree and React Router will perform matching again. This time it will successfully match the `/a` path and the navigation will complete successfully.
In the above example, if the user clicks a link to `/a`, React Router won't be able to match it initially and will call `patchRoutesOnNavigation` with `/a` and a `matches` array containing the root route match. By calling `patch`, the `a` route will be added to the route tree and React Router will perform matching again. This time it will successfully match the `/a` path and the navigation will complete successfully.

**Patching new root-level routes**

Expand All @@ -455,7 +458,10 @@ const router = createBrowserRouter(
},
],
{
async unstable_patchRoutesOnMiss({ path, patch }) {
async unstable_patchRoutesOnNavigation({
path,
patch,
}) {
if (path === "/root-sibling") {
// Load/patch the `/root-sibling` route as a sibling of the root route
let route = await getRootSiblingRoute();
Expand All @@ -480,7 +486,10 @@ let router = createBrowserRouter(
},
],
{
async unstable_patchRoutesOnMiss({ path, patch }) {
async unstable_patchRoutesOnNavigation({
path,
patch,
}) {
if (path.startsWith("/dashboard")) {
let children = await import("./dashboard");
patch(null, children);
Expand Down Expand Up @@ -510,7 +519,7 @@ let router = createBrowserRouter(
children: [
{
// If we want to include /dashboard in the critical routes, we need to
// also include it's index route since patchRoutesOnMiss will not be
// also include it's index route since patchRoutesOnNavigation will not be
// called on a navigation to `/dashboard` because it will have successfully
// matched the `/dashboard` parent route
index: true,
Expand All @@ -535,7 +544,10 @@ let router = createBrowserRouter(
},
],
{
async unstable_patchRoutesOnMiss({ matches, patch }) {
async unstable_patchRoutesOnNavigation({
matches,
patch,
}) {
let leafRoute = matches[matches.length - 1]?.route;
if (leafRoute?.handle?.lazyChildren) {
let children =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe("v7_partialHydration", () => {
testPartialHydration(createMemoryRouter, ReactRouter_RouterProvider);

// these tests only run for memory since we just need to set initialEntries
it("supports partial hydration w/patchRoutesOnMiss (leaf fallback)", async () => {
it("supports partial hydration w/patchRoutesOnNavigation (leaf fallback)", async () => {
let parentDfd = createDeferred();
let childDfd = createDeferred();
let router = createMemoryRouter(
Expand Down Expand Up @@ -69,7 +69,7 @@ describe("v7_partialHydration", () => {
future: {
v7_partialHydration: true,
},
unstable_patchRoutesOnMiss({ path, patch }) {
unstable_patchRoutesOnNavigation({ path, patch }) {
if (path === "/parent/child") {
patch("parent", [
{
Expand Down Expand Up @@ -119,7 +119,7 @@ describe("v7_partialHydration", () => {
`);
});

it("supports partial hydration w/patchRoutesOnMiss (root fallback)", async () => {
it("supports partial hydration w/patchRoutesOnNavigation (root fallback)", async () => {
let parentDfd = createDeferred();
let childDfd = createDeferred();
let router = createMemoryRouter(
Expand Down Expand Up @@ -157,7 +157,7 @@ describe("v7_partialHydration", () => {
future: {
v7_partialHydration: true,
},
unstable_patchRoutesOnMiss({ path, patch }) {
unstable_patchRoutesOnNavigation({ path, patch }) {
if (path === "/parent/child") {
patch("parent", [
{
Expand Down
10 changes: 5 additions & 5 deletions packages/react-router-dom/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
RouterProps,
RouterProviderProps,
To,
unstable_PatchRoutesOnMissFunction,
unstable_PatchRoutesOnNavigationFunction,
} from "react-router";
import {
Router,
Expand Down Expand Up @@ -153,7 +153,7 @@ export type {
To,
UIMatch,
unstable_HandlerResult,
unstable_PatchRoutesOnMissFunction,
unstable_PatchRoutesOnNavigationFunction,
} from "react-router";
export {
AbortedDeferredError,
Expand Down Expand Up @@ -261,7 +261,7 @@ interface DOMRouterOpts {
future?: Partial<Omit<RouterFutureConfig, "v7_prependBasename">>;
hydrationData?: HydrationState;
unstable_dataStrategy?: unstable_DataStrategyFunction;
unstable_patchRoutesOnMiss?: unstable_PatchRoutesOnMissFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
window?: Window;
}

Expand All @@ -280,7 +280,7 @@ export function createBrowserRouter(
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
unstable_patchRoutesOnMiss: opts?.unstable_patchRoutesOnMiss,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
window: opts?.window,
}).initialize();
}
Expand All @@ -300,7 +300,7 @@ export function createHashRouter(
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
unstable_patchRoutesOnMiss: opts?.unstable_patchRoutesOnMiss,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
window: opts?.window,
}).initialize();
}
Expand Down
10 changes: 5 additions & 5 deletions packages/react-router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import type {
To,
UIMatch,
unstable_HandlerResult,
unstable_AgnosticPatchRoutesOnMissFunction,
unstable_AgnosticPatchRoutesOnNavigationFunction,
} from "@remix-run/router";
import {
AbortedDeferredError,
Expand Down Expand Up @@ -291,8 +291,8 @@ function mapRouteProperties(route: RouteObject) {
return updates;
}

export interface unstable_PatchRoutesOnMissFunction
extends unstable_AgnosticPatchRoutesOnMissFunction<RouteMatch> {}
export interface unstable_PatchRoutesOnNavigationFunction
extends unstable_AgnosticPatchRoutesOnNavigationFunction<RouteMatch> {}

export function createMemoryRouter(
routes: RouteObject[],
Expand All @@ -303,7 +303,7 @@ export function createMemoryRouter(
initialEntries?: InitialEntry[];
initialIndex?: number;
unstable_dataStrategy?: unstable_DataStrategyFunction;
unstable_patchRoutesOnMiss?: unstable_PatchRoutesOnMissFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
}
): RemixRouter {
return createRouter({
Expand All @@ -320,7 +320,7 @@ export function createMemoryRouter(
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
unstable_patchRoutesOnMiss: opts?.unstable_patchRoutesOnMiss,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
}).initialize();
}

Expand Down
2 changes: 1 addition & 1 deletion packages/react-router/lib/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ export function _renderMatches(
dataRouterState.matches.length > 0
) {
// Don't bail if we're initializing with partial hydration and we have
// router matches. That means we're actively running `patchRoutesOnMiss`
// router matches. That means we're actively running `patchRoutesOnNavigation`
// so we should render down the partial matches to the appropriate
// `HydrateFallback`. We only do this if `parentMatches` is empty so it
// only impacts the root matches for `RouterProvider` and no descendant
Expand Down
Loading

0 comments on commit 5a6545b

Please sign in to comment.