Skip to content

Commit

Permalink
Stabilize APIs (#9980)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 authored Oct 9, 2024
1 parent 6ee69c1 commit 2678a01
Show file tree
Hide file tree
Showing 26 changed files with 114 additions and 112 deletions.
14 changes: 14 additions & 0 deletions .changeset/rare-shoes-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@remix-run/dev": minor
"@remix-run/react": minor
"@remix-run/server-runtime": minor
---

Stabilize React Router APIs in Remix
- Adopt stabilized React Router APIs internally
- Single Fetch: `unstable_dataStrategy` -> `dataStrategy`
- Lazy Route Discovery: `unstable_patchRoutesOnNavigation` -> `patchRoutesOnNavigation`
- Stabilize public-facing APIs
- Single Fetch: `unstable_data()` -> `data()`
- `unstable_viewTransition` -> `viewTransition` (`Link`, `Form`, `navigate`, `submit`)
- `unstable_flushSync>` -> `<Link viewTransition>` (`Link`, `Form`, `navigate`, `submit`, `useFetcher`)
8 changes: 2 additions & 6 deletions docs/components/form.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,9 @@ If true, it will submit the form with the browser instead of client side routing

This is recommended over [`<form>`][form_element]. When the `action` prop is omitted, `<Form>` and `<form>` will sometimes call different actions depending on what the current URL is since `<form>` uses the current URL as the default, but `<Form>` uses the URL for the route the form is rendered in.

### `unstable_viewTransition`
### `viewTransition`

The `unstable_viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in [`document.startViewTransition()`][document-start-view-transition]. If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state].

<docs-warning>
Please note that this API is marked unstable and may be subject to breaking changes without a major release.
</docs-warning>
The `viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in [`document.startViewTransition()`][document-start-view-transition]. If you need to apply specific styles for this view transition, you will also need to leverage the [`useViewTransitionState()`][use-view-transition-state].

## Notes

Expand Down
17 changes: 6 additions & 11 deletions docs/components/link.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,24 +185,23 @@ function SomeComp() {

This state is inaccessible on the server as it is implemented on top of [`history.state`][history-state].

## `unstable_viewTransition`
## `viewTransition`

The `unstable_viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in [`document.startViewTransition()`][document-start-view-transition]:
The `viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in [`document.startViewTransition()`][document-start-view-transition]:

```jsx
<Link to={to} unstable_viewTransition>
<Link to={to} viewTransition>
Click me
</Link>
```

If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state]:
If you need to apply specific styles for this view transition, you will also need to leverage the [`useViewTransitionState()`][use-view-transition-state]:

```jsx
function ImageLink(to) {
const isTransitioning =
unstable_useViewTransitionState(to);
const isTransitioning = useViewTransitionState(to);
return (
<Link to={to} unstable_viewTransition>
<Link to={to} viewTransition>
<p
style={{
viewTransitionName: isTransitioning
Expand All @@ -226,10 +225,6 @@ function ImageLink(to) {
}
```

<docs-warning>
Please note that this API is marked unstable and may be subject to breaking changes without a major release.
</docs-warning>

[scroll-restoration-component]: ./scroll-restoration
[history-state]: https://developer.mozilla.org/en-US/docs/Web/API/History/state
[view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
Expand Down
16 changes: 6 additions & 10 deletions docs/components/nav-link.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ a.pending {

### `.transitioning`

A `transitioning` class is added to a [`<NavLink unstable_viewTransition>`][view-transition-prop] component when it is transitioning during a navigation, so you can use CSS to style it.
A `transitioning` class is added to a [`<NavLink viewTransition>`][view-transition-prop] component when it is transitioning during a navigation, so you can use CSS to style it.

```tsx
<NavLink to="/messages" unstable_viewTransition />
<NavLink to="/messages" viewTransition />
```

```css
Expand Down Expand Up @@ -136,9 +136,9 @@ Adding the `caseSensitive` prop changes the matching logic to make it case-sensi
| `<NavLink to="/SpOnGe-bOB" />` | `/sponge-bob` | true |
| `<NavLink to="/SpOnGe-bOB" caseSensitive />` | `/sponge-bob` | false |

## `unstable_viewTransition`
## `viewTransition`

The `unstable_viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in [`document.startViewTransition()`][document-start-view-transition]. By default, during the transition a [`transitioning` class][transitioning-class] will be added to the [`<a>` element][a-element] that you can use to customize the view transition.
The `viewTransition` prop enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in [`document.startViewTransition()`][document-start-view-transition]. By default, during the transition a [`transitioning` class][transitioning-class] will be added to the [`<a>` element][a-element] that you can use to customize the view transition.

```css
a.transitioning p {
Expand All @@ -151,7 +151,7 @@ a.transitioning img {
```

```tsx
<NavLink to={to} unstable_viewTransition>
<NavLink to={to} viewTransition>
<p>Image Number {idx}</p>
<img src={src} alt={`Img ${idx}`} />
</NavLink>
Expand All @@ -160,7 +160,7 @@ a.transitioning img {
You may also use the [`className`][class-name-prop]/[`style`][style-prop] props or the render props passed to [`children`][children-prop] to further customize based on the `isTransitioning` value.

```tsx
<NavLink to={to} unstable_viewTransition>
<NavLink to={to} viewTransition>
{({ isTransitioning }) => (
<>
<p
Expand All @@ -186,10 +186,6 @@ You may also use the [`className`][class-name-prop]/[`style`][style-prop] props
</NavLink>
```

<docs-warning>
Please note that this API is marked unstable and may be subject to breaking changes without a major release.
</docs-warning>

### `<Link>` props

All other props of [`<Link>`][link-component] are supported.
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/single-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ For v2, you may still continue returning normal `Response` instances and their `
Over time, you should start eliminating returned Responses from your loaders and actions.

- If your `loader`/`action` was returning `json`/`defer` without setting any `status`/`headers`, then you can just remove the call to `json`/`defer` and return the data directly
- If your `loader`/`action` was returning custom `status`/`headers` via `json`/`defer`, you should switch those to use the new [`unstable_data()`][data-utility] utility.
- If your `loader`/`action` was returning custom `status`/`headers` via `json`/`defer`, you should switch those to use the new [`data()`][data-utility] utility.

### Client Loaders

Expand Down
4 changes: 2 additions & 2 deletions docs/hooks/use-fetcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ fetcher.load("/some/route?foo=bar");

`fetcher.load`'s revalidate by default after action submissions and explicit revalidation requests via [`useRevalidator`][userevalidator]. Because `fetcher.load` loads a specific URL they don't revalidate on changes to route param or URL search param. You can use [`shouldRevalidate`][shouldrevalidate] to optimize which data should be reloaded.

#### `options.unstable_flushSync`
#### `options.flushSync`

The `unstable_flushSync` option tells React Router DOM to wrap the initial state update for this `fetcher.load` in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.
The `flushSync` option tells React Router DOM to wrap the initial state update for this `fetcher.load` in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]. This allows you to perform synchronous DOM actions immediately after the update is flushed to the DOM.

<docs-warning>`ReactDOM.flushSync` de-optimizes React and can hurt the performance of your app.</docs-warning>

Expand Down
6 changes: 3 additions & 3 deletions docs/hooks/use-navigate.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ navigate(".", {
- `"route"` will use the route hierarchy so `".."` will remove all URL segments of the current route pattern while `"path"` will use the URL path so `".."` will remove one URL segment
- **state**: any - adds persistent client side routing state to the next location
- **preventScrollReset**: boolean - if you are using [`<ScrollRestoration>`][scroll-restoration], prevent the scroll position from being reset to the top of the window when navigating
- **unstable_flushSync**: boolean - wraps the initial state update for this navigation in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]
- **unstable_viewTransition**: boolean - enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`
- If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state]
- **flushSync**: boolean - wraps the initial state update for this navigation in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]
- **viewTransition**: boolean - enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`
- If you need to apply specific styles for this view transition, you will also need to leverage the [`useViewTransitionState()`][use-view-transition-state]

[redirect]: ../utils/redirect
[flush-sync]: https://react.dev/reference/react-dom/flushSync
Expand Down
6 changes: 3 additions & 3 deletions docs/hooks/use-submit.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ Options for the submission, the same as [`<Form>`][form-component] props. All op
- **preventScrollReset**: Prevents the scroll position from being reset to the top of the window when the data is submitted. Default is `false`.
- **replace**: Replaces the current entry in the history stack, instead of pushing the new entry. Default is `false`.
- **relative**: Defines relative route resolution behavior. Either `"route"` (relative to the route hierarchy) or `"path"` (relative to the URL).
- **unstable_flushSync**: Wraps the initial state update for this navigation in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]
- **unstable_viewTransition**: Enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`
- If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state]
- **flushSync**: Wraps the initial state update for this navigation in a [`ReactDOM.flushSync`][flush-sync] call instead of the default [`React.startTransition`][start-transition]
- **viewTransition**: Enables a [View Transition][view-transitions] for this navigation by wrapping the final state update in `document.startViewTransition()`
- If you need to apply specific styles for this view transition, you will also need to leverage the [`useViewTransitionState()`][use-view-transition-state]

```tsx
submit(data, {
Expand Down
16 changes: 8 additions & 8 deletions docs/hooks/use-view-transition-state.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
---
title: unstable_useViewTransitionState
title: useViewTransitionState
toc: false
---

# `unstable_useViewTransitionState`
# `useViewTransitionState`

This hook returns `true` when there is an active [View Transition][view-transitions] to the specified location. This can be used to apply finer-grained styles to elements to further customize the view transition. This requires that view transitions have been enabled for the given navigation via the `unstable_viewTransition` prop on the [`Link`][link-component-view-transition] (or the [`Form`][form-component-view-transition], [`NavLink`][nav-link-component-view-transition], `navigate`, or `submit` call).
This hook returns `true` when there is an active [View Transition][view-transitions] to the specified location. This can be used to apply finer-grained styles to elements to further customize the view transition. This requires that view transitions have been enabled for the given navigation via the `viewTransition` prop on the [`Link`][link-component-view-transition] (or the [`Form`][form-component-view-transition], [`NavLink`][nav-link-component-view-transition], `navigate`, or `submit` call).

Consider clicking on an image in a list that you need to expand into the hero image on the destination page:

```jsx
function NavImage({ src, alt, id }) {
const to = `/images/${idx}`;
const vt = unstable_useViewTransitionState(href);
const vt = useViewTransitionState(href);
return (
<Link to={to} unstable_viewTransition>
<Link to={to} viewTransition>
<img
src={src}
alt={alt}
Expand All @@ -28,6 +28,6 @@ function NavImage({ src, alt, id }) {
```

[view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
[link-component-view-transition]: ../components/link#unstable_viewtransition
[form-component-view-transition]: ../components/form#unstable_viewtransition
[nav-link-component-view-transition]: ../components/nav-link#unstable_viewtransition
[link-component-view-transition]: ../components/link#viewtransition
[form-component-view-transition]: ../components/form#viewtransition
[nav-link-component-view-transition]: ../components/nav-link#viewtransition
6 changes: 3 additions & 3 deletions docs/utils/data.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: unstable_data
title: data
toc: false
---

# `unstable_data`
# `data`

This is a utility for use with [Single Fetch][single-fetch] to return raw data accompanied with a status code or custom response headers. This avoids the need to serialize your data into a `Response` instance to provide custom status/headers. This is generally a replacement for `loader`/`action` functions that used [`json`][json] or [`defer`][defer] prior to Single Fetch.

```tsx
import { unstable_data as data } from "@remix-run/node"; // or cloudflare/deno
import { data } from "@remix-run/node"; // or cloudflare/deno

export const loader = async () => {
return data(
Expand Down
2 changes: 1 addition & 1 deletion integration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@remix-run/dev": "workspace:*",
"@remix-run/express": "workspace:*",
"@remix-run/node": "workspace:*",
"@remix-run/router": "1.19.2",
"@remix-run/router": "1.20.0-pre.0",
"@remix-run/server-runtime": "workspace:*",
"@types/express": "^4.17.9",
"@vanilla-extract/css": "^1.10.0",
Expand Down
18 changes: 9 additions & 9 deletions integration/single-fetch-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const files = {
`,

"app/routes/data-with-response.tsx": js`
import { useActionData, useLoaderData, unstable_data as data } from "@remix-run/react";
import { useActionData, useLoaderData, data } from "@remix-run/react";
export function headers ({ actionHeaders, loaderHeaders, errorHeaders }) {
if ([...actionHeaders].length > 0) {
Expand Down Expand Up @@ -276,7 +276,7 @@ test.describe("single-fetch", () => {
});
});

test("loads proper data (via unstable_data) on single fetch loader requests", async () => {
test("loads proper data (via data) on single fetch loader requests", async () => {
let fixture = await createFixture({
config: {
future: {
Expand Down Expand Up @@ -310,7 +310,7 @@ test.describe("single-fetch", () => {
});
});

test("loads proper data (via unstable_data) on single fetch action requests", async () => {
test("loads proper data (via data) on single fetch action requests", async () => {
let fixture = await createFixture({
config: {
future: {
Expand Down Expand Up @@ -565,7 +565,7 @@ test.describe("single-fetch", () => {
expect(urls).toEqual([]);
});

test("does not revalidate on 4xx/5xx action responses (via unstable_data)", async ({
test("does not revalidate on 4xx/5xx action responses (via data)", async ({
page,
}) => {
let fixture = await createFixture({
Expand All @@ -577,21 +577,21 @@ test.describe("single-fetch", () => {
files: {
...files,
"app/routes/action.tsx": js`
import { Form, Link, useActionData, useLoaderData, useNavigation, unstable_data } from '@remix-run/react';
import { Form, Link, useActionData, useLoaderData, useNavigation, data } from '@remix-run/react';
export async function action({ request }) {
let fd = await request.formData();
if (fd.get('throw') === "5xx") {
throw unstable_data("Thrown 500", { status: 500 });
throw data("Thrown 500", { status: 500 });
}
if (fd.get('throw') === "4xx") {
throw unstable_data("Thrown 400", { status: 400 });
throw data("Thrown 400", { status: 400 });
}
if (fd.get('return') === "5xx") {
return unstable_data("Returned 500", { status: 500 });
return data("Returned 500", { status: 500 });
}
if (fd.get('return') === "4xx") {
return unstable_data("Returned 400", { status: 400 });
return data("Returned 400", { status: 400 });
}
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/remix-cloudflare/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export {
export {
createRequestHandler,
createSession,
unstable_data,
data,
defer,
broadcastDevReady,
logDevReady,
Expand Down
2 changes: 1 addition & 1 deletion packages/remix-deno/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export {
replace,
unstable_composeUploadHandlers,
unstable_createMemoryUploadHandler,
unstable_data,
data,
unstable_parseMultipartFormData,
} from "@remix-run/server-runtime";

Expand Down
2 changes: 1 addition & 1 deletion packages/remix-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@mdx-js/mdx": "^2.3.0",
"@npmcli/package-json": "^4.0.1",
"@remix-run/node": "workspace:*",
"@remix-run/router": "1.19.2",
"@remix-run/router": "1.20.0-pre.0",
"@remix-run/server-runtime": "workspace:*",
"@types/mdx": "^2.0.5",
"@vanilla-extract/integration": "^6.2.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/remix-node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export {
export {
createRequestHandler,
createSession,
unstable_data,
data,
defer,
broadcastDevReady,
logDevReady,
Expand Down
4 changes: 2 additions & 2 deletions packages/remix-react/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,14 @@ export function RemixBrowser(_props: RemixBrowserProps): ReactElement {
},
hydrationData,
mapRouteProperties,
unstable_dataStrategy: window.__remixContext.future.unstable_singleFetch
dataStrategy: window.__remixContext.future.unstable_singleFetch
? getSingleFetchDataStrategy(
window.__remixManifest,
window.__remixRouteModules,
() => router
)
: undefined,
unstable_patchRoutesOnNavigation: getPatchRoutesOnNavigationFunction(
patchRoutesOnNavigation: getPatchRoutesOnNavigationFunction(
window.__remixManifest,
window.__remixRouteModules,
window.__remixContext.future,
Expand Down
4 changes: 2 additions & 2 deletions packages/remix-react/fog-of-war.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Router } from "@remix-run/router";
import { matchRoutes } from "@remix-run/router";
import * as React from "react";
import type { unstable_PatchRoutesOnNavigationFunction } from "react-router";
import type { PatchRoutesOnNavigationFunction } from "react-router";

import type { AssetsManifest, FutureConfig } from "./entry";
import type { RouteModules } from "./routeModules";
Expand Down Expand Up @@ -70,7 +70,7 @@ export function getPatchRoutesOnNavigationFunction(
future: FutureConfig,
isSpaMode: boolean,
basename: string | undefined
): unstable_PatchRoutesOnNavigationFunction | undefined {
): PatchRoutesOnNavigationFunction | undefined {
if (!isFogOfWarEnabled(future, isSpaMode)) {
return undefined;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/remix-react/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export {
useSearchParams,
useSubmit,
useBlocker,
useViewTransitionState,
unstable_usePrompt,
unstable_useViewTransitionState,
} from "react-router-dom";
export {
// For use in clientLoader/clientAction
Expand All @@ -66,7 +66,7 @@ export {
redirect,
redirectDocument,
replace,
unstable_data,
data,
} from "@remix-run/server-runtime";

export type { RemixBrowserProps } from "./browser";
Expand Down
Loading

0 comments on commit 2678a01

Please sign in to comment.