Skip to content

Commit

Permalink
Add future flag to docs
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed Dec 5, 2023
1 parent 51f5f7f commit 576be9e
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .changeset/relative-splat-path.md
Expand Up @@ -42,7 +42,7 @@ function Dashboard() {
<Route path="/" element={<DashboardHome />} />
<Route path="team" element={<DashboardTeam />} />
<Route path="projects" element={<DashboardProjects />} />
</Router>
</Routes>
</div>
);
}
Expand Down
3 changes: 3 additions & 0 deletions docs/components/form.md
Expand Up @@ -109,6 +109,8 @@ If you need to post to a different route, then add an action prop:

- [Index Search Param][indexsearchparam] (index vs parent route disambiguation)

<docs-info>Please see the [Splat Paths][relativesplatpath] section on the `useResolvedPath` docs for a note on the behavior of the `future.v7_relativeSplatPath` future flag for relative `useNavigate()` behavior within splat routes</docs-info>

## `method`

This determines the [HTTP verb](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) to be used. The same as plain HTML [form method][htmlform-method], except it also supports "put", "patch", and "delete" in addition to "get" and "post". The default is "get".
Expand Down Expand Up @@ -394,3 +396,4 @@ You can access those values from the `request.url`
[history-state]: https://developer.mozilla.org/en-US/docs/Web/API/History/state
[use-view-transition-state]: ../hooks//use-view-transition-state
[view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
[relativesplatpath]: ../hooks/use-resolved-path#splat-paths
3 changes: 3 additions & 0 deletions docs/components/link.md
Expand Up @@ -63,6 +63,8 @@ A relative `<Link to>` value (that does not begin with `/`) resolves relative to

<docs-info>`<Link to>` with a `..` behaves differently from a normal `<a href>` when the current URL ends with `/`. `<Link to>` ignores the trailing slash, and removes one URL segment for each `..`. But an `<a href>` value handles `..` differently when the current URL ends with `/` vs when it does not.</docs-info>

<docs-info>Please see the [Splat Paths][relativesplatpath] section on the `useResolvedPath` docs for a note on the behavior of the `future.v7_relativeSplatPath` future flag for relative `<Link to>` behavior within splat routes</docs-info>

## `relative`

By default, links are relative to the route hierarchy (`relative="route"`), so `..` will go up one `Route` level. Occasionally, you may find that you have matching URL patterns that do not make sense to be nested, and you'd prefer to use relative _path_ routing. You can opt into this behavior with `relative="path"`:
Expand Down Expand Up @@ -201,3 +203,4 @@ function ImageLink(to) {
[view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API
[picking-a-router]: ../routers/picking-a-router
[navlink]: ./nav-link
[relativesplatpath]: ../hooks/use-resolved-path#splat-paths
14 changes: 8 additions & 6 deletions docs/guides/api-development-strategy.md
Expand Up @@ -63,12 +63,13 @@ const router = createBrowserRouter(routes, {
});
```

| Flag | Description |
| ----------------------------------------- | --------------------------------------------------------------------- |
| `v7_fetcherPersist` | Delay active fetcher cleanup until they return to an `idle` state |
| `v7_normalizeFormMethod` | Normalize `useNavigation().formMethod` to be an uppercase HTTP Method |
| [`v7_partialHydration`][partialhydration] | Support partial hydration for Server-rendered apps |
| `v7_prependBasename` | Prepend the router basename to navigate/fetch paths |
| Flag | Description |
| ------------------------------------------- | --------------------------------------------------------------------- |
| `v7_fetcherPersist` | Delay active fetcher cleanup until they return to an `idle` state |
| `v7_normalizeFormMethod` | Normalize `useNavigation().formMethod` to be an uppercase HTTP Method |
| [`v7_partialHydration`][partialhydration] | Support partial hydration for Server-rendered apps |
| `v7_prependBasename` | Prepend the router basename to navigate/fetch paths |
| [`v7_relativeSplatPath`][relativesplatpath] | Fix buggy relative path resolution in splat routes |

### React Router Future Flags

Expand Down Expand Up @@ -96,3 +97,4 @@ These flags apply to both Data and non-Data Routers and are passed to the render
[picking-a-router]: ../routers/picking-a-router
[starttransition]: https://react.dev/reference/react/startTransition
[partialhydration]: ../routers/create-browser-router#partial-hydration-data
[relativesplatpath]: ../hooks/use-resolved-path#splat-paths
10 changes: 5 additions & 5 deletions docs/hooks/use-href.md
Expand Up @@ -18,8 +18,8 @@ declare function useHref(

The `useHref` hook returns a URL that may be used to link to the given `to` location, even outside of React Router.

> **Tip:**
>
> You may be interested in taking a look at the source for the `<Link>`
> component in `react-router-dom` to see how it uses `useHref` internally to
> determine its own `href` value.
<docs-info>You may be interested in taking a look at the source for the `<Link>` component in `react-router-dom` to see how it uses `useHref` internally to determine its own `href` value</docs-info>

<docs-info>Please see the [Splat Paths][relativesplatpath] section on the `useResolvedPath` docs for a note on the behavior of the `future.v7_relativeSplatPath` future flag for relative `useHref()` behavior within splat routes</docs-info>

[relativesplatpath]: ../hooks/use-resolved-path#splat-paths
3 changes: 3 additions & 0 deletions docs/hooks/use-navigate.md
Expand Up @@ -54,6 +54,8 @@ The `navigate` function has two signatures:
- Either pass a `To` value (same type as `<Link to>`) with an optional second `options` argument (similar to the props you can pass to [`<Link>`][link]), or
- Pass the delta you want to go in the history stack. For example, `navigate(-1)` is equivalent to hitting the back button

<docs-info>Please see the [Splat Paths][relativesplatpath] section on the `useResolvedPath` docs for a note on the behavior of the `future.v7_relativeSplatPath` future flag for relative `useNavigate()` behavior within splat routes</docs-info>

## `options.replace`

Specifying `replace: true` will cause the navigation to replace the current entry in the history stack instead of adding a new one.
Expand Down Expand Up @@ -119,3 +121,4 @@ The `unstable_viewTransition` option enables a [View Transition][view-transition
[picking-a-router]: ../routers/picking-a-router
[flush-sync]: https://react.dev/reference/react-dom/flushSync
[start-transition]: https://react.dev/reference/react/startTransition
[relativesplatpath]: ../hooks/use-resolved-path#splat-paths
59 changes: 59 additions & 0 deletions docs/hooks/use-resolved-path.md
Expand Up @@ -22,5 +22,64 @@ This is useful when building links from relative values. For example, check out

See [resolvePath][resolvepath] for more information.

## Splat Paths

The original logic for `useResolvedPath` behaved differently for splat paths which in hindsight was incorrect/buggy behavior. This was fixed in [`6.19.0`][release-6.19.0] but it was determined that a large number of existing applications [relied on this behavior][revert-comment] so the fix was reverted in [`6.20.1`][release-6.20.1] and re-introduced in [`6.21.0`][release-6.21.0] behind a `future.v7_relativeSplatPath` [future flag][future-flag]. This will become the default behavior in React Router v7, so it is recommended to update your applications at your convenience to be better prepared for the eventual v7 upgrade.

It should be noted that this is the foundation for all relative routing in React Router, so this applies to the following relative path code flows as well:

- `<Link to>`
- `useNavigate()`
- `useHref()`
- `<Form action>`
- `useSubmit()`
- Relative path `redirect` responses returned from loaders and actions

### Behavior without the flag

When this flag is not enabled, the default behavior is that when resolving relative paths inside of a [splat route (`*`)][splat], the splat portion of the path is ignored. So, given a route tree such as:

```jsx
<BrowserRouter>
<Routes>
<Route path="/dashboard/*" element={<Dashboard />} />
</Routes>
</BrowserRouter>
```

If you are currently at URL `/dashboard/teams`, `useResolvedPath("projects")` inside the `Dashboard` component would resolve to `/dashboard/projects` because the "current" location we are relative to would be considered `/dashboard` _without the "teams" splat value_.

This makes for a slight convenience in routing between "sibling" splat routes (`/dashboard/teams`, `/dashboard/projects`, etc.), however it causes other inconsistencies such as:

- `useResolvedPath(".")` no longer resolves to the current location for that route, it actually resolved you "up" to `/dashboard` from `/dashboard/teams`
- If you changed your route definition to use a dynamic parameter (`<Route path="/dashboard/:widget">`), then any resolved paths inside the `Dashboard` component would break since the dynamic param value is not ignored like the splat value

And then it gets worse if you define the splat route as a child:

```jsx
<BrowserRouter>
<Routes>
<Route path="/dashboard">
<Route path="*" element={<Dashboard />} />
</Route>
</Routes>
</BrowserRouter>
```

- Now, `useResolvedPath(".")` and `useResolvedPath("..")` resolve to the exact same path inside `<Dashboard />`
- If you were using a Data Router and defined an `action` on the splat route, you'd get a 405 error on `<Form>` submissions because they (by default) submit to `"."` which would resolve to the parent `/dashboard` route which doesn't have an `action`.

### Behavior with the flag

When you enable the flag, this "bug" is fixed so that path resolution is consistent across all route types, `useResolvedPath(".")` always resolves to the current pathname for the contextual route. This includes any dynamic param or splat param values.

If you want to navigate between "sibling" routes within a splat route, it is suggested you move your splat route to it's own child (`<Route path="*" element={<Dashboard />} />`) and use `useResolvedPath("../teams")` and `useResolvedPath("../projects")` parent-relative paths to navigate to sibling routes.

[navlink]: ../components/nav-link
[resolvepath]: ../utils/resolve-path
[release-6.19.0]: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6190
[release-6.20.1]: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6201
[release-6.21.0]: https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6210
[revert-comment]: https://github.com/remix-run/react-router/issues/11052#issuecomment-1836589329
[future-flag]: ../guides/api-development-strategy
[splat]: ../route/route#splats
3 changes: 3 additions & 0 deletions docs/hooks/use-submit.md
Expand Up @@ -150,6 +150,8 @@ submit(null, {
<Form action="/logout" method="post" />;
```

<docs-info>Please see the [Splat Paths][relativesplatpath] section on the `useResolvedPath` docs for a note on the behavior of the `future.v7_relativeSplatPath` future flag for relative `useSubmit()` `action` behavior within splat routes</docs-info>

Because submissions are navigations, the options may also contain the other navigation related props from [`<Form>`][form] such as:

- `fetcherKey`
Expand All @@ -170,3 +172,4 @@ The `unstable_flushSync` option tells React Router DOM to wrap the initial state
[form]: ../components/form
[flush-sync]: https://react.dev/reference/react-dom/flushSync
[start-transition]: https://react.dev/reference/react/startTransition
[relativesplatpath]: ../hooks/use-resolved-path#splat-paths

0 comments on commit 576be9e

Please sign in to comment.