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 unstable_dataStrategy #11974

Merged
merged 3 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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/nasty-queens-leave.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`
20 changes: 10 additions & 10 deletions docs/routers/create-browser-router.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function createBrowserRouter(
basename?: string;
future?: FutureConfig;
hydrationData?: HydrationState;
unstable_dataStrategy?: unstable_DataStrategyFunction;
dataStrategy?: DataStrategyFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
window?: Window;
}
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 DataStrategyResult {

### Overview

`unstable_dataStrategy` receives the same arguments as a `loader`/`action` (`request`, `params`) but it also receives 2 new parameters: `matches` and `fetcherKey`:
`dataStrategy` receives the same arguments as a `loader`/`action` (`request`, `params`) but it also receives 2 new parameters: `matches` and `fetcherKey`:

- **`matches`** - An array of the matched routes where each match is extended with 2 new fields for use in the data strategy function:
- **`match.shouldLoad`** - A boolean value indicating whether this route handler should be called in this pass
Expand All @@ -247,9 +247,9 @@ interface DataStrategyResult {
- It is safe to call `match.resolve` for all matches, even if they have `shouldLoad=false`, and it will no-op if no loading is required
- You should generally always call `match.resolve()` for `shouldLoad:true` routes to ensure that any `route.lazy` implementations are processed
- See the examples below for how to implement custom handler execution via `match.resolve`
- **`fetcherKey`** - The key of the fetcher we are calling `unstable_dataStrategy` for, otherwise `null` for navigational executions
- **`fetcherKey`** - The key of the fetcher we are calling `dataStrategy` for, otherwise `null` for navigational executions

The `dataStrategy` function should return a key/value object of `routeId -> DataStrategyResult` and should include entries for any routes where a handler was executed. A `DataStrategyResult` indicates if the handler was successful or not based on the `DataStrategyResult["type"]` field. If the returned `DataStrategyResult["result"]` is a `Response`, React Router will unwrap it for you (via `res.json` or `res.text`). If you need to do custom decoding of a `Response` but want to preserve the status code, you can use the `unstable_data` utility to return your decoded data along with a `ResponseInit`.
The `dataStrategy` function should return a key/value object of `routeId -> DataStrategyResult` and should include entries for any routes where a handler was executed. A `DataStrategyResult` indicates if the handler was successful or not based on the `DataStrategyResult["type"]` field. If the returned `DataStrategyResult["result"]` is a `Response`, React Router will unwrap it for you (via `res.json` or `res.text`). If you need to do custom decoding of a `Response` but want to preserve the status code, you can use the `data` utility to return your decoded data along with a `ResponseInit`.

### Example Use Cases

Expand All @@ -259,7 +259,7 @@ In the simplest case, let's look at hooking into this API to add some logging fo

```ts
let router = createBrowserRouter(routes, {
async unstable_dataStrategy({ request, matches }) {
async dataStrategy({ request, matches }) {
// Grab only the matches we need to run handlers for
const matchesToLoad = matches.filter(
(m) => m.shouldLoad
Expand Down Expand Up @@ -290,7 +290,7 @@ If you want to avoid the `reduce`, you can manually build up the `results` objec

```ts
let router = createBrowserRouter(routes, {
async unstable_dataStrategy({ request, matches }) {
async dataStrategy({ request, matches }) {
const matchesToLoad = matches.filter(
(m) => m.shouldLoad
);
Expand Down Expand Up @@ -353,7 +353,7 @@ const routes = [
];

let router = createBrowserRouter(routes, {
async unstable_dataStrategy({
async dataStrategy({
request,
params,
matches,
Expand Down Expand Up @@ -426,7 +426,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 Down
8 changes: 4 additions & 4 deletions packages/react-router-dom-v5-compat/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ export type {
ActionFunctionArgs,
AwaitProps,
BrowserRouterProps,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
unstable_DataStrategyResult,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
DataStrategyResult,
DataRouteMatch,
DataRouteObject,
ErrorResponse,
Expand Down
16 changes: 8 additions & 8 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_DataStrategyFunction,
DataStrategyFunction,
unstable_PatchRoutesOnNavigationFunction,
} from "react-router";
import {
Expand Down Expand Up @@ -106,10 +106,10 @@ export type {
BlockerFunction,
DataRouteMatch,
DataRouteObject,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
unstable_DataStrategyResult,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
DataStrategyResult,
ErrorResponse,
Fetcher,
FutureConfig,
Expand Down Expand Up @@ -258,7 +258,7 @@ interface DOMRouterOpts {
basename?: string;
future?: Partial<Omit<RouterFutureConfig, "v7_prependBasename">>;
hydrationData?: HydrationState;
unstable_dataStrategy?: unstable_DataStrategyFunction;
dataStrategy?: DataStrategyFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
window?: Window;
}
Expand All @@ -277,7 +277,7 @@ export function createBrowserRouter(
hydrationData: opts?.hydrationData || parseHydrationData(),
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
dataStrategy: opts?.dataStrategy,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
window: opts?.window,
}).initialize();
Expand All @@ -297,7 +297,7 @@ export function createHashRouter(
hydrationData: opts?.hydrationData || parseHydrationData(),
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
dataStrategy: opts?.dataStrategy,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
window: opts?.window,
}).initialize();
Expand Down
8 changes: 4 additions & 4 deletions packages/react-router-native/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ export type {
BlockerFunction,
DataRouteMatch,
DataRouteObject,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
unstable_DataStrategyResult,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
DataStrategyResult,
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
20 changes: 10 additions & 10 deletions packages/react-router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type {
ActionFunctionArgs,
Blocker,
BlockerFunction,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
unstable_DataStrategyResult,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
DataStrategyResult,
ErrorResponse,
Fetcher,
HydrationState,
Expand Down Expand Up @@ -136,10 +136,10 @@ export type {
AwaitProps,
DataRouteMatch,
DataRouteObject,
unstable_DataStrategyFunction,
unstable_DataStrategyFunctionArgs,
unstable_DataStrategyMatch,
unstable_DataStrategyResult,
DataStrategyFunction,
DataStrategyFunctionArgs,
DataStrategyMatch,
DataStrategyResult,
ErrorResponse,
Fetcher,
FutureConfig,
Expand Down Expand Up @@ -302,7 +302,7 @@ export function createMemoryRouter(
hydrationData?: HydrationState;
initialEntries?: InitialEntry[];
initialIndex?: number;
unstable_dataStrategy?: unstable_DataStrategyFunction;
dataStrategy?: DataStrategyFunction;
unstable_patchRoutesOnNavigation?: unstable_PatchRoutesOnNavigationFunction;
}
): RemixRouter {
Expand All @@ -319,7 +319,7 @@ export function createMemoryRouter(
hydrationData: opts?.hydrationData,
routes,
mapRouteProperties,
unstable_dataStrategy: opts?.unstable_dataStrategy,
dataStrategy: opts?.dataStrategy,
unstable_patchRoutesOnNavigation: opts?.unstable_patchRoutesOnNavigation,
}).initialize();
}
Expand Down
4 changes: 2 additions & 2 deletions packages/router/__tests__/ssr-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1602,7 +1602,7 @@ describe("ssr", () => {
let { query } = createStaticHandler(SSR_ROUTES);

let context = await query(createRequest("/custom"), {
unstable_dataStrategy: urlDataStrategy,
dataStrategy: urlDataStrategy,
});
expect(context).toMatchObject({
actionData: null,
Expand Down Expand Up @@ -2635,7 +2635,7 @@ describe("ssr", () => {
let data;

data = await queryRoute(createRequest("/custom"), {
unstable_dataStrategy: urlDataStrategy,
dataStrategy: urlDataStrategy,
});
expect(data).toBeInstanceOf(URLSearchParams);
expect((data as URLSearchParams).get("foo")).toBe("bar");
Expand Down
2 changes: 1 addition & 1 deletion packages/router/__tests__/utils/data-router-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ export function setup({
hydrationData,
future,
window: testWindow,
unstable_dataStrategy: dataStrategy,
dataStrategy: dataStrategy,
}).initialize();

function getRouteHelpers(
Expand Down
10 changes: 5 additions & 5 deletions packages/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export type {
AgnosticNonIndexRouteObject,
AgnosticRouteMatch,
AgnosticRouteObject,
DataStrategyFunction as unstable_DataStrategyFunction,
DataStrategyFunctionArgs as unstable_DataStrategyFunctionArgs,
DataStrategyMatch as unstable_DataStrategyMatch,
DataStrategyResult as unstable_DataStrategyResult,
DataStrategyFunction as DataStrategyFunction,
DataStrategyFunctionArgs as DataStrategyFunctionArgs,
DataStrategyMatch as DataStrategyMatch,
DataStrategyResult as DataStrategyResult,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aliases aren't necessary now. 👍

ErrorResponse,
FormEncType,
FormMethod,
Expand All @@ -38,7 +38,7 @@ export type {

export {
AbortedDeferredError,
data as unstable_data,
data,
defer,
generatePath,
getToPathname,
Expand Down
Loading