Skip to content
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

Stabilize dataStrategy/patchRoutesOnNavigation APIs #11891

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/six-impalas-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"react-router-dom": minor
"react-router": minor
"@remix-run/router": minor
---

Stabilize `unstable_dataStrategy`, `unstable_data`, `unstable_patchRoutesOnNavigation` and their associated types
32 changes: 16 additions & 16 deletions docs/routers/create-browser-router.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ function createBrowserRouter(
basename?: string;
future?: FutureConfig;
hydrationData?: HydrationState;
unstable_dataStrategy?: unstable_DataStrategyFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
dataStrategy?: DataStrategyFunction;
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
window?: Window;
}
): RemixRouter;
Expand Down Expand Up @@ -184,15 +184,15 @@ const router = createBrowserRouter(
);
```

## `opts.unstable_dataStrategy`
## `opts.dataStrategy`

<docs-warning>This is a low-level API intended for advanced use-cases. This overrides React Router's internal handling of `loader`/`action` execution, and if done incorrectly will break your app code. Please use with caution and perform the appropriate testing.</docs-warning>

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

By default, React Router is opinionated about how your data is loaded/submitted - and most notably, executes all of your loaders in parallel for optimal data fetching. While we think this is the right behavior for most use-cases, we realize that there is no "one size fits all" solution when it comes to data fetching for the wide landscape of application requirements.

The `unstable_dataStrategy` option gives you full control over how your loaders and actions are executed and lays the foundation to build in more advanced APIs such as middleware, context, and caching layers. Over time, we expect that we'll leverage this API internally to bring more first class APIs to React Router, but until then (and beyond), this is your way to add more advanced functionality for your applications data needs.
The `dataStrategy` option gives you full control over how your loaders and actions are executed and lays the foundation to build in more advanced APIs such as middleware, context, and caching layers. Over time, we expect that we'll leverage this API internally to bring more first class APIs to React Router, but until then (and beyond), this is your way to add more advanced functionality for your applications data needs.

### Type Declaration

Expand Down Expand Up @@ -232,7 +232,7 @@ interface HandlerResult {

### Overview

`unstable_dataStrategy` receives the same arguments as a `loader`/`action` (`request`, `params`) but it also receives a `matches` array which is an array of the matched routes where each match is extended with 2 new fields for use in the data strategy function:
`dataStrategy` receives the same arguments as a `loader`/`action` (`request`, `params`) but it also receives a `matches` array which is an array of the matched routes where each match is extended with 2 new fields for use in the data strategy function:

- **`match.resolve`** - An async function that will resolve any `route.lazy` implementations and execute the route's handler (if necessary), returning a `HandlerResult`
- You should call `match.resolve` for _all_ matches every time to ensure that all lazy routes are properly resolved
Expand All @@ -256,7 +256,7 @@ In the simplest case, let's look at hooking into this API to add some logging fo

```ts
let router = createBrowserRouter(routes, {
unstable_dataStrategy({ request, matches }) {
dataStrategy({ request, matches }) {
return Promise.all(
matches.map(async (match) => {
console.log(`Processing route ${match.route.id}`);
Expand Down Expand Up @@ -307,7 +307,7 @@ const routes = [
];

let router = createBrowserRouter(routes, {
async unstable_dataStrategy({
async dataStrategy({
request,
params,
matches,
Expand Down Expand Up @@ -373,7 +373,7 @@ const routes = [
];

let router = createBrowserRouter(routes, {
unstable_dataStrategy({ request, params, matches }) {
dataStrategy({ request, params, matches }) {
// Compose route fragments into a single GQL payload
let gql = getFragmentsFromRouteHandles(matches);
let data = await fetchGql(gql);
Expand All @@ -384,7 +384,7 @@ let router = createBrowserRouter(routes, {
});
```

## `opts.unstable_patchRoutesOnNavigation`
## `opts.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_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.
This is where `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_PatchRoutesOnNavigationFunction {
export interface PatchRoutesOnNavigationFunction {
(opts: {
path: string;
matches: RouteMatch[];
Expand All @@ -413,7 +413,7 @@ export interface unstable_PatchRoutesOnNavigationFunction {

### Overview

`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.
`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,7 @@ const router = createBrowserRouter(
},
],
{
async unstable_patchRoutesOnNavigation({
async patchRoutesOnNavigation({
path,
patch,
}) {
Expand Down Expand Up @@ -458,7 +458,7 @@ const router = createBrowserRouter(
},
],
{
async unstable_patchRoutesOnNavigation({
async patchRoutesOnNavigation({
path,
patch,
}) {
Expand Down Expand Up @@ -486,7 +486,7 @@ let router = createBrowserRouter(
},
],
{
async unstable_patchRoutesOnNavigation({
async patchRoutesOnNavigation({
path,
patch,
}) {
Expand Down Expand Up @@ -544,7 +544,7 @@ let router = createBrowserRouter(
},
],
{
async unstable_patchRoutesOnNavigation({
async patchRoutesOnNavigation({
matches,
patch,
}) {
Expand Down
6 changes: 3 additions & 3 deletions packages/react-router-dom-v5-compat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ export type {
ActionFunctionArgs,
AwaitProps,
BrowserRouterProps,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
DataRouteMatch,
DataRouteObject,
ErrorResponse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ describe("v7_partialHydration", () => {
future: {
v7_partialHydration: true,
},
unstable_patchRoutesOnNavigation({ path, patch }) {
patchRoutesOnNavigation({ path, patch }) {
if (path === "/parent/child") {
patch("parent", [
{
Expand Down Expand Up @@ -157,7 +157,7 @@ describe("v7_partialHydration", () => {
future: {
v7_partialHydration: true,
},
unstable_patchRoutesOnNavigation({ path, patch }) {
patchRoutesOnNavigation({ path, patch }) {
if (path === "/parent/child") {
patch("parent", [
{
Expand Down
28 changes: 14 additions & 14 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_PatchRoutesOnNavigationFunction,
PatchRoutesOnNavigationFunction,
} from "react-router";
import {
Router,
Expand All @@ -38,9 +38,9 @@ import {
} from "react-router";
import type {
BrowserHistory,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
Fetcher,
FormEncType,
FormMethod,
Expand Down Expand Up @@ -89,9 +89,9 @@ import {
////////////////////////////////////////////////////////////////////////////////

export type {
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
FormEncType,
FormMethod,
GetScrollRestorationKeyFunction,
Expand Down Expand Up @@ -153,7 +153,7 @@ export type {
To,
UIMatch,
unstable_HandlerResult,
unstable_PatchRoutesOnNavigationFunction,
PatchRoutesOnNavigationFunction,
} from "react-router";
export {
AbortedDeferredError,
Expand Down Expand Up @@ -260,8 +260,8 @@ interface DOMRouterOpts {
basename?: string;
future?: Partial<Omit<RouterFutureConfig, "v7_prependBasename">>;
hydrationData?: HydrationState;
unstable_dataStrategy?: unstable_DataStrategyFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
dataStrategy?: DataStrategyFunction;
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
window?: Window;
}

Expand All @@ -279,8 +279,8 @@ export function createBrowserRouter(
hydrationData: opts?.hydrationData || parseHydrationData(),
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
dataStrategy: opts?.dataStrategy,
patchRoutesOnNavigation: opts?.patchRoutesOnNavigation,
window: opts?.window,
}).initialize();
}
Expand All @@ -299,8 +299,8 @@ export function createHashRouter(
hydrationData: opts?.hydrationData || parseHydrationData(),
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
dataStrategy: opts?.dataStrategy,
patchRoutesOnNavigation: opts?.patchRoutesOnNavigation,
window: opts?.window,
}).initialize();
}
Expand Down
6 changes: 3 additions & 3 deletions packages/react-router-native/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ export type {
BlockerFunction,
DataRouteMatch,
DataRouteObject,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
ErrorResponse,
Fetcher,
FutureConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3210,7 +3210,7 @@ describe("createMemoryRouter", () => {
/>
</Route>
),
{ initialEntries: ["/foo"], unstable_dataStrategy: urlDataStrategy }
{ initialEntries: ["/foo"], dataStrategy: urlDataStrategy }
);
let { container } = render(<RouterProvider router={router} />);

Expand Down
26 changes: 13 additions & 13 deletions packages/react-router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import type {
ActionFunctionArgs,
Blocker,
BlockerFunction,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
ErrorResponse,
Fetcher,
HydrationState,
Expand All @@ -32,7 +32,7 @@ import type {
To,
UIMatch,
unstable_HandlerResult,
unstable_AgnosticPatchRoutesOnNavigationFunction,
AgnosticPatchRoutesOnNavigationFunction,
} from "@remix-run/router";
import {
AbortedDeferredError,
Expand Down Expand Up @@ -136,9 +136,9 @@ export type {
AwaitProps,
DataRouteMatch,
DataRouteObject,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
ErrorResponse,
Fetcher,
FutureConfig,
Expand Down Expand Up @@ -291,8 +291,8 @@ function mapRouteProperties(route: RouteObject) {
return updates;
}

export interface unstable_PatchRoutesOnNavigationFunction
extends unstable_AgnosticPatchRoutesOnNavigationFunction<RouteMatch> {}
export interface PatchRoutesOnNavigationFunction
extends AgnosticPatchRoutesOnNavigationFunction<RouteMatch> {}

export function createMemoryRouter(
routes: RouteObject[],
Expand All @@ -302,8 +302,8 @@ export function createMemoryRouter(
hydrationData?: HydrationState;
initialEntries?: InitialEntry[];
initialIndex?: number;
unstable_dataStrategy?: unstable_DataStrategyFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
dataStrategy?: DataStrategyFunction;
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
}
): RemixRouter {
return createRouter({
Expand All @@ -319,8 +319,8 @@ export function createMemoryRouter(
hydrationData: opts?.hydrationData,
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
dataStrategy: opts?.dataStrategy,
patchRoutesOnNavigation: opts?.patchRoutesOnNavigation,
}).initialize();
}

Expand Down
Loading