From f9b3dbd9cbf513366c456b33d95227f42f36da63 Mon Sep 17 00:00:00 2001 From: "remix-cla-bot[bot]" <92060565+remix-cla-bot[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 16:40:27 +0000 Subject: [PATCH 01/15] chore: sort contributors list --- contributors.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributors.yml b/contributors.yml index 5cba918a57..ffc7e6fa58 100644 --- a/contributors.yml +++ b/contributors.yml @@ -111,8 +111,8 @@ - johnpangalos - jonkoops - jrakotoharisoa -- kachun333 - juanpprieto +- kachun333 - kantuni - kark - KAROTT7 From 7ce38dc49ee997706902ac2d033ba1fd683cfed0 Mon Sep 17 00:00:00 2001 From: Tomas R Date: Tue, 3 Oct 2023 21:56:29 +0200 Subject: [PATCH 02/15] [Docs]: Use consistent feature warnings (#10908) --- contributors.yml | 1 + docs/route/action.md | 2 +- docs/route/error-element.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contributors.yml b/contributors.yml index ffc7e6fa58..fdb075a3fd 100644 --- a/contributors.yml +++ b/contributors.yml @@ -216,6 +216,7 @@ - tkindy - tlinhart - tom-sherman +- tomasr8 - triangularcube - trungpv1601 - turansky diff --git a/docs/route/action.md b/docs/route/action.md index 8204dcd2af..7c8f697ff6 100644 --- a/docs/route/action.md +++ b/docs/route/action.md @@ -7,7 +7,7 @@ new: true Route actions are the "writes" to route [loader][loader] "reads". They provide a way for apps to perform data mutations with simple HTML and HTTP semantics while React Router abstracts away the complexity of asynchronous UI and revalidation. This gives you the simple mental model of HTML + HTTP (where the browser handles the asynchrony and revalidation) with the behavior and UX capabilities of modern SPAs. -This feature only works if using a data router like [`createBrowserRouter`][createbrowserrouter] +This feature only works if using a data router, see [Picking a Router][pickingarouter] ```tsx If you do not wish to specify a React element (i.e., `errorElement={}`) you may specify an `ErrorBoundary` component instead (i.e., `ErrorBoundary={MyErrorBoundary}`) and React Router will call `createElement` for you internally. -This feature only works if using a data router like [`createBrowserRouter`][createbrowserrouter] +This feature only works if using a data router, see [Picking a Router][pickingarouter] ```tsx Date: Wed, 11 Oct 2023 16:49:12 -0400 Subject: [PATCH 03/15] Enter prerelease mode --- .changeset/pre.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .changeset/pre.json diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..3acc19d115 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,12 @@ +{ + "mode": "pre", + "tag": "pre", + "initialVersions": { + "react-router": "6.16.0", + "react-router-dom": "6.16.0", + "react-router-dom-v5-compat": "6.16.0", + "react-router-native": "6.16.0", + "@remix-run/router": "1.9.0" + }, + "changesets": [] +} From b09c5d09198b1ee4a8bfbf8a2a8910fc8eed7d2c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 16:57:55 -0400 Subject: [PATCH 04/15] chore: Update version for release (pre) (#10924) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 8 ++- .../react-router-dom-v5-compat/CHANGELOG.md | 8 +++ .../react-router-dom-v5-compat/package.json | 4 +- packages/react-router-dom/CHANGELOG.md | 52 +++++++++++++++++++ packages/react-router-dom/package.json | 6 +-- packages/react-router-native/CHANGELOG.md | 7 +++ packages/react-router-native/package.json | 4 +- packages/react-router/CHANGELOG.md | 51 ++++++++++++++++++ packages/react-router/package.json | 4 +- packages/router/CHANGELOG.md | 50 ++++++++++++++++++ packages/router/package.json | 2 +- 11 files changed, 185 insertions(+), 11 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 3acc19d115..0a1e182251 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -8,5 +8,11 @@ "react-router-native": "6.16.0", "@remix-run/router": "1.9.0" }, - "changesets": [] + "changesets": [ + "404-root-with-path", + "error-response-type", + "partial-future-config", + "soft-forks-cough", + "start-view-transition" + ] } diff --git a/packages/react-router-dom-v5-compat/CHANGELOG.md b/packages/react-router-dom-v5-compat/CHANGELOG.md index 5610fd8a0d..2d2d9c2571 100644 --- a/packages/react-router-dom-v5-compat/CHANGELOG.md +++ b/packages/react-router-dom-v5-compat/CHANGELOG.md @@ -1,5 +1,13 @@ # `react-router-dom-v5-compat` +## 6.17.0-pre.0 + +### Patch Changes + +- Updated dependencies: + - `react-router@6.17.0-pre.0` + - `react-router-dom@6.17.0-pre.0` + ## 6.16.0 ### Minor Changes diff --git a/packages/react-router-dom-v5-compat/package.json b/packages/react-router-dom-v5-compat/package.json index d70c9e0778..9f2bbf9685 100644 --- a/packages/react-router-dom-v5-compat/package.json +++ b/packages/react-router-dom-v5-compat/package.json @@ -1,6 +1,6 @@ { "name": "react-router-dom-v5-compat", - "version": "6.16.0", + "version": "6.17.0-pre.0", "description": "Migration path to React Router v6 from v4/5", "keywords": [ "react", @@ -24,7 +24,7 @@ "types": "./dist/index.d.ts", "dependencies": { "history": "^5.3.0", - "react-router": "6.16.0" + "react-router": "6.17.0-pre.0" }, "peerDependencies": { "react": ">=16.8", diff --git a/packages/react-router-dom/CHANGELOG.md b/packages/react-router-dom/CHANGELOG.md index 1b63f809ce..bc8b02a8d9 100644 --- a/packages/react-router-dom/CHANGELOG.md +++ b/packages/react-router-dom/CHANGELOG.md @@ -1,5 +1,57 @@ # `react-router-dom` +## 6.17.0-pre.0 + +### Minor Changes + +- Add support for the [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) via `document.startViewTransition` to enable CSS animated transitions on SPA navigations in your application. ([#10916](https://github.com/remix-run/react-router/pull/10916)) + + The simplest approach to enabling a View Transition in your React Router app is via the new `` prop. This will cause the navigation DOM update to be wrapped in `document.startViewTransition` which will enable transitions for the DOM update. Without any additional CSS styles, you'll get a basic cross-fade animation for your page. + + If you need to apply more fine-grained styles for your animations, you can leverage the `unstable_useViewTransitionState` hook which will tell you when a transition is in progress and you can use that to apply classes or styles: + + ```jsx + function ImageLink(to, src, alt) { + let isTransitioning = unstable_useViewTransitionState(to); + return ( + + {alt} + + ); + } + ``` + + You can also use the `` shorthand which will manage the hook usage for you and automatically add a `transitioning` class to the `` during the transition: + + ```css + a.transitioning img { + view-transition-name: "image-expand"; + } + ``` + + ```jsx + + {alt} + + ``` + + For an example usage of View Transitions with React Router, check out [our fork](https://github.com/brophdawg11/react-router-records) of the [Astro Records](https://github.com/Charca/astro-records) demo. + + For more information on using the View Transitions API, please refer to the [Smooth and simple transitions with the View Transitions API](https://developer.chrome.com/docs/web-platform/view-transitions/) guide from the Google Chrome team. + +### Patch Changes + +- Log a warning and fail gracefully in `ScrollRestoration` when `sessionStorage` is unavailable ([#10848](https://github.com/remix-run/react-router/pull/10848)) +- Updated dependencies: + - `@remix-run/router@1.10.0-pre.0` + - `react-router@6.17.0-pre.0` + ## 6.16.0 ### Minor Changes diff --git a/packages/react-router-dom/package.json b/packages/react-router-dom/package.json index 4a5b08d839..84d190896e 100644 --- a/packages/react-router-dom/package.json +++ b/packages/react-router-dom/package.json @@ -1,6 +1,6 @@ { "name": "react-router-dom", - "version": "6.16.0", + "version": "6.17.0-pre.0", "description": "Declarative routing for React web applications", "keywords": [ "react", @@ -23,8 +23,8 @@ "module": "./dist/index.js", "types": "./dist/index.d.ts", "dependencies": { - "@remix-run/router": "1.9.0", - "react-router": "6.16.0" + "@remix-run/router": "1.10.0-pre.0", + "react-router": "6.17.0-pre.0" }, "devDependencies": { "react": "^18.2.0", diff --git a/packages/react-router-native/CHANGELOG.md b/packages/react-router-native/CHANGELOG.md index c998f0c2e4..53d4abe85d 100644 --- a/packages/react-router-native/CHANGELOG.md +++ b/packages/react-router-native/CHANGELOG.md @@ -1,5 +1,12 @@ # `react-router-native` +## 6.17.0-pre.0 + +### Patch Changes + +- Updated dependencies: + - `react-router@6.17.0-pre.0` + ## 6.16.0 ### Minor Changes diff --git a/packages/react-router-native/package.json b/packages/react-router-native/package.json index 41b5c3347c..82d086b230 100644 --- a/packages/react-router-native/package.json +++ b/packages/react-router-native/package.json @@ -1,6 +1,6 @@ { "name": "react-router-native", - "version": "6.16.0", + "version": "6.17.0-pre.0", "description": "Declarative routing for React Native applications", "keywords": [ "react", @@ -22,7 +22,7 @@ "types": "./dist/index.d.ts", "dependencies": { "@ungap/url-search-params": "^0.2.2", - "react-router": "6.16.0" + "react-router": "6.17.0-pre.0" }, "devDependencies": { "react": "^18.2.0", diff --git a/packages/react-router/CHANGELOG.md b/packages/react-router/CHANGELOG.md index 95cffeb86d..96abafd0fb 100644 --- a/packages/react-router/CHANGELOG.md +++ b/packages/react-router/CHANGELOG.md @@ -1,5 +1,56 @@ # `react-router` +## 6.17.0-pre.0 + +### Minor Changes + +- Add support for the [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) via `document.startViewTransition` to enable CSS animated transitions on SPA navigations in your application. ([#10916](https://github.com/remix-run/react-router/pull/10916)) + + The simplest approach to enabling a View Transition in your React Router app is via the new `` prop. This will cause the navigation DOM update to be wrapped in `document.startViewTransition` which will enable transitions for the DOM update. Without any additional CSS styles, you'll get a basic cross-fade animation for your page. + + If you need to apply more fine-grained styles for your animations, you can leverage the `unstable_useViewTransitionState` hook which will tell you when a transition is in progress and you can use that to apply classes or styles: + + ```jsx + function ImageLink(to, src, alt) { + let isTransitioning = unstable_useViewTransitionState(to); + return ( + + {alt} + + ); + } + ``` + + You can also use the `` shorthand which will manage the hook usage for you and automatically add a `transitioning` class to the `` during the transition: + + ```css + a.transitioning img { + view-transition-name: "image-expand"; + } + ``` + + ```jsx + + {alt} + + ``` + + For an example usage of View Transitions with React Router, check out [our fork](https://github.com/brophdawg11/react-router-records) of the [Astro Records](https://github.com/Charca/astro-records) demo. + + For more information on using the View Transitions API, please refer to the [Smooth and simple transitions with the View Transitions API](https://developer.chrome.com/docs/web-platform/view-transitions/) guide from the Google Chrome team. + +### Patch Changes + +- Fix `RouterProvider` `future` prop type to be a `Partial` so that not all flags must be specified ([#10900](https://github.com/remix-run/react-router/pull/10900)) +- Updated dependencies: + - `@remix-run/router@1.10.0-pre.0` + ## 6.16.0 ### Minor Changes diff --git a/packages/react-router/package.json b/packages/react-router/package.json index e4412d028f..6a40e8c0b1 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "react-router", - "version": "6.16.0", + "version": "6.17.0-pre.0", "description": "Declarative routing for React", "keywords": [ "react", @@ -23,7 +23,7 @@ "module": "./dist/index.js", "types": "./dist/index.d.ts", "dependencies": { - "@remix-run/router": "1.9.0" + "@remix-run/router": "1.10.0-pre.0" }, "devDependencies": { "react": "^18.2.0" diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md index d524687114..8ff3ab411a 100644 --- a/packages/router/CHANGELOG.md +++ b/packages/router/CHANGELOG.md @@ -1,5 +1,55 @@ # `@remix-run/router` +## 1.10.0-pre.0 + +### Minor Changes + +- Add support for the [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) via `document.startViewTransition` to enable CSS animated transitions on SPA navigations in your application. ([#10916](https://github.com/remix-run/react-router/pull/10916)) + + The simplest approach to enabling a View Transition in your React Router app is via the new `` prop. This will cause the navigation DOM update to be wrapped in `document.startViewTransition` which will enable transitions for the DOM update. Without any additional CSS styles, you'll get a basic cross-fade animation for your page. + + If you need to apply more fine-grained styles for your animations, you can leverage the `unstable_useViewTransitionState` hook which will tell you when a transition is in progress and you can use that to apply classes or styles: + + ```jsx + function ImageLink(to, src, alt) { + let isTransitioning = unstable_useViewTransitionState(to); + return ( + + {alt} + + ); + } + ``` + + You can also use the `` shorthand which will manage the hook usage for you and automatically add a `transitioning` class to the `` during the transition: + + ```css + a.transitioning img { + view-transition-name: "image-expand"; + } + ``` + + ```jsx + + {alt} + + ``` + + For an example usage of View Transitions with React Router, check out [our fork](https://github.com/brophdawg11/react-router-records) of the [Astro Records](https://github.com/Charca/astro-records) demo. + + For more information on using the View Transitions API, please refer to the [Smooth and simple transitions with the View Transitions API](https://developer.chrome.com/docs/web-platform/view-transitions/) guide from the Google Chrome team. + +### Patch Changes + +- Allow 404 detection to leverage root route error boundary if path contains a URL segment ([#10852](https://github.com/remix-run/react-router/pull/10852)) +- Fix `ErrorResponse` type to avoid leaking internal field ([#10876](https://github.com/remix-run/react-router/pull/10876)) + ## 1.9.0 ### Minor Changes diff --git a/packages/router/package.json b/packages/router/package.json index 6fb89b8c6a..6bb5053c00 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@remix-run/router", - "version": "1.9.0", + "version": "1.10.0-pre.0", "description": "Nested/Data-driven/Framework-agnostic Routing", "keywords": [ "remix", From e93e9088360e3fc1a4183efc5a39c8e680903554 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Thu, 12 Oct 2023 13:31:43 -0400 Subject: [PATCH 05/15] Docs updates --- docs/components/link.md | 5 ++++- docs/components/nav-link.md | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/components/link.md b/docs/components/link.md index 948d2310ef..77b03253d1 100644 --- a/docs/components/link.md +++ b/docs/components/link.md @@ -153,13 +153,16 @@ The `unstable_viewTransition` prop enables a [View Transition][view-transitions] ```jsx + Click me + ``` If you need to apply specific styles for this view transition, you will also need to leverage the [`unstable_useViewTransitionState()`][use-view-transition-state]: ```jsx function ImageLink(to) { - let isTransitioning = unstable_useViewTransitionState(to); + const isTransitioning = + unstable_useViewTransitionState(to); return (

Date: Thu, 12 Oct 2023 14:34:28 -0400 Subject: [PATCH 06/15] Update docs for startViewTransition (#10927) --- docs/components/form.md | 4 +--- docs/components/link.md | 10 ++++++---- docs/components/nav-link.md | 4 ++-- docs/hooks/use-navigate.md | 7 ++++--- docs/routers/picking-a-router.md | 3 +++ 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/components/form.md b/docs/components/form.md index 2aa940cab3..5d1c6bbc0b 100644 --- a/docs/components/form.md +++ b/docs/components/form.md @@ -273,9 +273,7 @@ See also: [``][link-preventscrollreset] The `unstable_viewTransition` prop 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]. - -Please note that this API is marked unstable and may be subject to breaking changes without a major release. - +Please note that this API is marked unstable and may be subject to breaking changes without a major release # Examples diff --git a/docs/components/link.md b/docs/components/link.md index 77b03253d1..b32e02271c 100644 --- a/docs/components/link.md +++ b/docs/components/link.md @@ -157,7 +157,7 @@ The `unstable_viewTransition` prop enables a [View Transition][view-transitions] ``` -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 [`unstable_useViewTransitionState()`][use-view-transition-state] hook (or check out the `transitioning` class and `isTransitioning` render prop in [NavLink][navlink]): ```jsx function ImageLink(to) { @@ -188,9 +188,9 @@ function ImageLink(to) { } ``` - -Please note that this API is marked unstable and may be subject to breaking changes without a major release. - +`unstable_viewTransition` only works when using a data router, see [Picking a Router][picking-a-router] + +Please note that this API is marked unstable and may be subject to breaking changes without a major release [link-native]: ./link-native [scrollrestoration]: ./scroll-restoration @@ -199,3 +199,5 @@ Please note that this API is marked unstable and may be subject to breaking chan [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 +[picking-a-router]: ../routers/picking-a-router +[navlink]: ./nav-link diff --git a/docs/components/nav-link.md b/docs/components/nav-link.md index 937e855a85..e67c0079e5 100644 --- a/docs/components/nav-link.md +++ b/docs/components/nav-link.md @@ -171,9 +171,9 @@ You may also use the `className`/`style` props or the render props passed to `ch ``` - + Please note that this API is marked unstable and may be subject to breaking changes without a major release. - + [aria-current]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current [view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API diff --git a/docs/hooks/use-navigate.md b/docs/hooks/use-navigate.md index b07269409d..3f4334757e 100644 --- a/docs/hooks/use-navigate.md +++ b/docs/hooks/use-navigate.md @@ -89,9 +89,9 @@ function EditContact() { The `unstable_viewTransition` option 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]. - -Please note that this API is marked unstable and may be subject to breaking changes without a major release. - +`unstable_viewTransition` only works when using a data router, see [Picking a Router][picking-a-router] + +Please note that this API is marked unstable and may be subject to breaking changes without a major release [link]: ../components/link [redirect]: ../fetch/redirect @@ -101,3 +101,4 @@ Please note that this API is marked unstable and may be subject to breaking chan [scrollrestoration]: ../components/scroll-restoration [use-view-transition-state]: ../hooks//use-view-transition-state [view-transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API +[picking-a-router]: ../routers/picking-a-router diff --git a/docs/routers/picking-a-router.md b/docs/routers/picking-a-router.md index 42ce841928..92dfea59f5 100644 --- a/docs/routers/picking-a-router.md +++ b/docs/routers/picking-a-router.md @@ -109,6 +109,7 @@ The following APIs are introduced in React Router 6.4 and will only work when us - [`useRouteError`][userouteerror] - [`useRouteLoaderData`][userouteloaderdata] - [`useSubmit`][usesubmit] +- `startViewTransition` support on [Link][viewtransition-link] and [useNavigate][viewtransition-navigate] [createbrowserrouter]: ./create-browser-router [createhashrouter]: ./create-hash-router @@ -142,3 +143,5 @@ The following APIs are introduced in React Router 6.4 and will only work when us [userouteerror]: ../hooks/use-route-error [userouteloaderdata]: ../hooks/use-route-loader-data [usesubmit]: ../hooks/use-submit +[viewtransition-link]: ../components/link#unstable_viewtransition +[viewtransition-navigate]: ../hooks/use-navigate#optionsunstable_viewtransition From cbc9d7222cc4ca1e74f0b081472187bbd6a95a42 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Thu, 12 Oct 2023 17:16:49 -0400 Subject: [PATCH 07/15] Fix lint issues (#10930) --- .changeset/mighty-moles-compete.md | 5 +++++ examples/view-transitions/src/main.tsx | 1 + package.json | 6 +++--- packages/react-router/lib/components.tsx | 1 - 4 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/mighty-moles-compete.md diff --git a/.changeset/mighty-moles-compete.md b/.changeset/mighty-moles-compete.md new file mode 100644 index 0000000000..7b87ff129f --- /dev/null +++ b/.changeset/mighty-moles-compete.md @@ -0,0 +1,5 @@ +--- +"react-router": patch +--- + +[REMOVE] fix lint issues diff --git a/examples/view-transitions/src/main.tsx b/examples/view-transitions/src/main.tsx index 53dc063623..bfe23d1fc0 100644 --- a/examples/view-transitions/src/main.tsx +++ b/examples/view-transitions/src/main.tsx @@ -223,6 +223,7 @@ const router = createBrowserRouter([ }, ]); +// eslint-disable-next-line @typescript-eslint/no-unused-vars function NavImage({ src, idx }: { src: string; idx: number }) { let href = `/images/${idx}`; let vt = unstable_useViewTransitionState(href); diff --git a/package.json b/package.json index 1073d672b6..9e6cc7a28f 100644 --- a/package.json +++ b/package.json @@ -116,13 +116,13 @@ "none": "15.2 kB" }, "packages/react-router/dist/umd/react-router.production.min.js": { - "none": "17.6 kB" + "none": "17.61 kB" }, "packages/react-router-dom/dist/react-router-dom.production.min.js": { - "none": "13.6 kB" + "none": "13.61 kB" }, "packages/react-router-dom/dist/umd/react-router-dom.production.min.js": { - "none": "19.9 kB" + "none": "19.91 kB" } } } diff --git a/packages/react-router/lib/components.tsx b/packages/react-router/lib/components.tsx index 0c79d97932..86dfbae3cc 100644 --- a/packages/react-router/lib/components.tsx +++ b/packages/react-router/lib/components.tsx @@ -1,4 +1,3 @@ -import * as ReactDOM from "react-dom"; import type { InitialEntry, LazyRouteFunction, From b916689b4a211827cc324cf05994c334e25d380b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:19:54 -0400 Subject: [PATCH 08/15] chore: Update version for release (pre) (#10931) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 1 + packages/react-router-dom-v5-compat/CHANGELOG.md | 8 ++++++++ packages/react-router-dom-v5-compat/package.json | 4 ++-- packages/react-router-dom/CHANGELOG.md | 7 +++++++ packages/react-router-dom/package.json | 4 ++-- packages/react-router-native/CHANGELOG.md | 7 +++++++ packages/react-router-native/package.json | 4 ++-- packages/react-router/CHANGELOG.md | 6 ++++++ packages/react-router/package.json | 2 +- 9 files changed, 36 insertions(+), 7 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index 0a1e182251..20f626fc81 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -11,6 +11,7 @@ "changesets": [ "404-root-with-path", "error-response-type", + "mighty-moles-compete", "partial-future-config", "soft-forks-cough", "start-view-transition" diff --git a/packages/react-router-dom-v5-compat/CHANGELOG.md b/packages/react-router-dom-v5-compat/CHANGELOG.md index 2d2d9c2571..ca55fd7a69 100644 --- a/packages/react-router-dom-v5-compat/CHANGELOG.md +++ b/packages/react-router-dom-v5-compat/CHANGELOG.md @@ -1,5 +1,13 @@ # `react-router-dom-v5-compat` +## 6.17.0-pre.1 + +### Patch Changes + +- Updated dependencies: + - `react-router@6.17.0-pre.1` + - `react-router-dom@6.17.0-pre.1` + ## 6.17.0-pre.0 ### Patch Changes diff --git a/packages/react-router-dom-v5-compat/package.json b/packages/react-router-dom-v5-compat/package.json index 9f2bbf9685..d3545ec397 100644 --- a/packages/react-router-dom-v5-compat/package.json +++ b/packages/react-router-dom-v5-compat/package.json @@ -1,6 +1,6 @@ { "name": "react-router-dom-v5-compat", - "version": "6.17.0-pre.0", + "version": "6.17.0-pre.1", "description": "Migration path to React Router v6 from v4/5", "keywords": [ "react", @@ -24,7 +24,7 @@ "types": "./dist/index.d.ts", "dependencies": { "history": "^5.3.0", - "react-router": "6.17.0-pre.0" + "react-router": "6.17.0-pre.1" }, "peerDependencies": { "react": ">=16.8", diff --git a/packages/react-router-dom/CHANGELOG.md b/packages/react-router-dom/CHANGELOG.md index bc8b02a8d9..16b503d8c1 100644 --- a/packages/react-router-dom/CHANGELOG.md +++ b/packages/react-router-dom/CHANGELOG.md @@ -1,5 +1,12 @@ # `react-router-dom` +## 6.17.0-pre.1 + +### Patch Changes + +- Updated dependencies: + - `react-router@6.17.0-pre.1` + ## 6.17.0-pre.0 ### Minor Changes diff --git a/packages/react-router-dom/package.json b/packages/react-router-dom/package.json index 84d190896e..9bc2734d97 100644 --- a/packages/react-router-dom/package.json +++ b/packages/react-router-dom/package.json @@ -1,6 +1,6 @@ { "name": "react-router-dom", - "version": "6.17.0-pre.0", + "version": "6.17.0-pre.1", "description": "Declarative routing for React web applications", "keywords": [ "react", @@ -24,7 +24,7 @@ "types": "./dist/index.d.ts", "dependencies": { "@remix-run/router": "1.10.0-pre.0", - "react-router": "6.17.0-pre.0" + "react-router": "6.17.0-pre.1" }, "devDependencies": { "react": "^18.2.0", diff --git a/packages/react-router-native/CHANGELOG.md b/packages/react-router-native/CHANGELOG.md index 53d4abe85d..af44835bad 100644 --- a/packages/react-router-native/CHANGELOG.md +++ b/packages/react-router-native/CHANGELOG.md @@ -1,5 +1,12 @@ # `react-router-native` +## 6.17.0-pre.1 + +### Patch Changes + +- Updated dependencies: + - `react-router@6.17.0-pre.1` + ## 6.17.0-pre.0 ### Patch Changes diff --git a/packages/react-router-native/package.json b/packages/react-router-native/package.json index 82d086b230..11bb3b8695 100644 --- a/packages/react-router-native/package.json +++ b/packages/react-router-native/package.json @@ -1,6 +1,6 @@ { "name": "react-router-native", - "version": "6.17.0-pre.0", + "version": "6.17.0-pre.1", "description": "Declarative routing for React Native applications", "keywords": [ "react", @@ -22,7 +22,7 @@ "types": "./dist/index.d.ts", "dependencies": { "@ungap/url-search-params": "^0.2.2", - "react-router": "6.17.0-pre.0" + "react-router": "6.17.0-pre.1" }, "devDependencies": { "react": "^18.2.0", diff --git a/packages/react-router/CHANGELOG.md b/packages/react-router/CHANGELOG.md index 96abafd0fb..e8871e3ccc 100644 --- a/packages/react-router/CHANGELOG.md +++ b/packages/react-router/CHANGELOG.md @@ -1,5 +1,11 @@ # `react-router` +## 6.17.0-pre.1 + +### Patch Changes + +- [REMOVE] fix lint issues ([#10930](https://github.com/remix-run/react-router/pull/10930)) + ## 6.17.0-pre.0 ### Minor Changes diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 6a40e8c0b1..dc7220aea3 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "react-router", - "version": "6.17.0-pre.0", + "version": "6.17.0-pre.1", "description": "Declarative routing for React", "keywords": [ "react", From c48341d6b75f4fd5b0ec60ed32c3c45ebb1e532f Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Fri, 13 Oct 2023 15:44:18 -0400 Subject: [PATCH 09/15] Lift startViewTransition implementation to react-router-dom (#10928) --- .changeset/lift-start-view-transition.md | 6 + .changeset/start-view-transition.md | 1 - package.json | 8 +- .../__tests__/exports-test.tsx | 17 +- packages/react-router-dom/index.tsx | 337 ++++++++++++++++-- packages/react-router-dom/server.tsx | 21 +- packages/react-router/index.ts | 2 - packages/react-router/lib/components.tsx | 185 +--------- packages/react-router/lib/context.ts | 16 - 9 files changed, 358 insertions(+), 235 deletions(-) create mode 100644 .changeset/lift-start-view-transition.md diff --git a/.changeset/lift-start-view-transition.md b/.changeset/lift-start-view-transition.md new file mode 100644 index 0000000000..f469f68be4 --- /dev/null +++ b/.changeset/lift-start-view-transition.md @@ -0,0 +1,6 @@ +--- +"react-router-dom": patch +"react-router": patch +--- + +Export a separate `RouterProvider` from `react-router-dom` with `startViewTransition` support diff --git a/.changeset/start-view-transition.md b/.changeset/start-view-transition.md index 55a09e5f05..fe65a08e45 100644 --- a/.changeset/start-view-transition.md +++ b/.changeset/start-view-transition.md @@ -1,6 +1,5 @@ --- "react-router-dom": minor -"react-router": minor "@remix-run/router": minor --- diff --git a/package.json b/package.json index 9e6cc7a28f..f1746f97c7 100644 --- a/package.json +++ b/package.json @@ -113,16 +113,16 @@ "none": "48.3 kB" }, "packages/react-router/dist/react-router.production.min.js": { - "none": "15.2 kB" + "none": "13.9 kB" }, "packages/react-router/dist/umd/react-router.production.min.js": { - "none": "17.61 kB" + "none": "16.3 kB" }, "packages/react-router-dom/dist/react-router-dom.production.min.js": { - "none": "13.61 kB" + "none": "15.9 kB" }, "packages/react-router-dom/dist/umd/react-router-dom.production.min.js": { - "none": "19.91 kB" + "none": "22.1 kB" } } } diff --git a/packages/react-router-dom/__tests__/exports-test.tsx b/packages/react-router-dom/__tests__/exports-test.tsx index e3b479d6f1..dcf2e22ba7 100644 --- a/packages/react-router-dom/__tests__/exports-test.tsx +++ b/packages/react-router-dom/__tests__/exports-test.tsx @@ -6,16 +6,23 @@ let nonReExportedKeys = new Set([ "UNSAFE_useRoutesImpl", ]); +let modifiedExports = new Set(["RouterProvider"]); + describe("react-router-dom", () => { for (let key in ReactRouter) { - if (!nonReExportedKeys.has(key)) { - it(`re-exports ${key} from react-router`, () => { - expect(ReactRouterDOM[key]).toBe(ReactRouter[key]); - }); - } else { + if (nonReExportedKeys.has(key)) { it(`does not re-export ${key} from react-router`, () => { expect(ReactRouterDOM[key]).toBe(undefined); }); + } else if (modifiedExports.has(key)) { + it(`re-exports a different version of ${key}`, () => { + expect(ReactRouterDOM[key]).toBeDefined(); + expect(ReactRouterDOM[key]).not.toBe(ReactRouter[key]); + }); + } else { + it(`re-exports ${key} from react-router`, () => { + expect(ReactRouterDOM[key]).toBe(ReactRouter[key]); + }); } } }); diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx index 3f76a09afd..8c62c56893 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -4,12 +4,15 @@ */ import * as React from "react"; import type { + DataRouteObject, FutureConfig, Location, NavigateOptions, NavigationType, + Navigator, RelativeRoutingType, RouteObject, + RouterProviderProps, To, } from "react-router"; import { @@ -26,9 +29,9 @@ import { UNSAFE_DataRouterStateContext as DataRouterStateContext, UNSAFE_NavigationContext as NavigationContext, UNSAFE_RouteContext as RouteContext, - UNSAFE_ViewTransitionContext as ViewTransitionContext, UNSAFE_mapRouteProperties as mapRouteProperties, UNSAFE_useRouteId as useRouteId, + UNSAFE_useRoutesImpl as useRoutesImpl, } from "react-router"; import type { BrowserHistory, @@ -43,6 +46,8 @@ import type { HydrationState, Router as RemixRouter, V7_FormMethod, + RouterState, + RouterSubscriber, } from "@remix-run/router"; import { createRouter, @@ -143,7 +148,6 @@ export { Outlet, Route, Router, - RouterProvider, Routes, createMemoryRouter, createPath, @@ -203,13 +207,15 @@ export { UNSAFE_NavigationContext, UNSAFE_LocationContext, UNSAFE_RouteContext, - UNSAFE_ViewTransitionContext, UNSAFE_useRouteId, } from "react-router"; //#endregion declare global { var __staticRouterHydrationData: HydrationState | undefined; + interface Document { + startViewTransition(cb: () => Promise | void): ViewTransition; + } } //////////////////////////////////////////////////////////////////////////////// @@ -320,6 +326,31 @@ function deserializeErrors( //#endregion +//////////////////////////////////////////////////////////////////////////////// +//#region Contexts +//////////////////////////////////////////////////////////////////////////////// + +type ViewTransitionContextObject = + | { + isTransitioning: false; + } + | { + isTransitioning: true; + currentLocation: Location; + nextLocation: Location; + }; + +const ViewTransitionContext = React.createContext({ + isTransitioning: false, +}); +if (__DEV__) { + ViewTransitionContext.displayName = "ViewTransition"; +} + +export { ViewTransitionContext as UNSAFE_ViewTransitionContext }; + +//#endregion + //////////////////////////////////////////////////////////////////////////////// //#region Components //////////////////////////////////////////////////////////////////////////////// @@ -348,6 +379,245 @@ function deserializeErrors( const START_TRANSITION = "startTransition"; const startTransitionImpl = React[START_TRANSITION]; +function startTransitionSafe(cb: () => void) { + if (startTransitionImpl) { + startTransitionImpl(cb); + } else { + cb(); + } +} + +interface ViewTransition { + finished: Promise; + ready: Promise; + updateCallbackDone: Promise; + skipTransition(): void; +} + +class Deferred { + status: "pending" | "resolved" | "rejected" = "pending"; + promise: Promise; + // @ts-expect-error - no initializer + resolve: (value: T) => void; + // @ts-expect-error - no initializer + reject: (reason?: unknown) => void; + constructor() { + this.promise = new Promise((resolve, reject) => { + this.resolve = (value) => { + if (this.status === "pending") { + this.status = "resolved"; + resolve(value); + } + }; + this.reject = (reason) => { + if (this.status === "pending") { + this.status = "rejected"; + reject(reason); + } + }; + }); + } +} + +/** + * Given a Remix Router instance, render the appropriate UI + */ +export function RouterProvider({ + fallbackElement, + router, + future, +}: RouterProviderProps): React.ReactElement { + let [state, setStateImpl] = React.useState(router.state); + let [pendingState, setPendingState] = React.useState(); + let [vtContext, setVtContext] = React.useState({ + isTransitioning: false, + }); + let [renderDfd, setRenderDfd] = React.useState>(); + let [transition, setTransition] = React.useState(); + let [interruption, setInterruption] = React.useState<{ + state: RouterState; + currentLocation: Location; + nextLocation: Location; + }>(); + let { v7_startTransition } = future || {}; + + let optInStartTransition = React.useCallback( + (cb: () => void) => { + if (v7_startTransition) { + startTransitionSafe(cb); + } else { + cb(); + } + }, + [v7_startTransition] + ); + + let setState = React.useCallback( + ( + newState: RouterState, + { unstable_viewTransitionOpts: viewTransitionOpts } + ) => { + if ( + !viewTransitionOpts || + router.window == null || + typeof router.window.document.startViewTransition !== "function" + ) { + // Mid-navigation state update, or startViewTransition isn't available + optInStartTransition(() => setStateImpl(newState)); + } else if (transition && renderDfd) { + // Interrupting an in-progress transition, cancel and let everything flush + // out, and then kick off a new transition from the interruption state + renderDfd.resolve(); + transition.skipTransition(); + setInterruption({ + state: newState, + currentLocation: viewTransitionOpts.currentLocation, + nextLocation: viewTransitionOpts.nextLocation, + }); + } else { + // Completed navigation update with opted-in view transitions, let 'er rip + setPendingState(newState); + setVtContext({ + isTransitioning: true, + currentLocation: viewTransitionOpts.currentLocation, + nextLocation: viewTransitionOpts.nextLocation, + }); + } + }, + [optInStartTransition, transition, renderDfd, router.window] + ); + + // Need to use a layout effect here so we are subscribed early enough to + // pick up on any render-driven redirects/navigations (useEffect/) + React.useLayoutEffect(() => router.subscribe(setState), [router, setState]); + + // When we start a view transition, create a Deferred we can use for the + // eventual "completed" render + React.useEffect(() => { + if (vtContext.isTransitioning) { + setRenderDfd(new Deferred()); + } + }, [vtContext.isTransitioning]); + + // Once the deferred is created, kick off startViewTransition() to update the + // DOM and then wait on the Deferred to resolve (indicating the DOM update has + // happened) + React.useEffect(() => { + if (renderDfd && pendingState && router.window) { + let newState = pendingState; + let renderPromise = renderDfd.promise; + let transition = router.window.document.startViewTransition(async () => { + optInStartTransition(() => setStateImpl(newState)); + await renderPromise; + }); + transition.finished.finally(() => { + setRenderDfd(undefined); + setTransition(undefined); + setPendingState(undefined); + setVtContext({ isTransitioning: false }); + }); + setTransition(transition); + } + }, [optInStartTransition, pendingState, renderDfd, router.window]); + + // When the new location finally renders and is committed to the DOM, this + // effect will run to resolve the transition + React.useEffect(() => { + if ( + renderDfd && + pendingState && + state.location.key === pendingState.location.key + ) { + renderDfd.resolve(); + } + }, [renderDfd, transition, state.location, pendingState]); + + // If we get interrupted with a new navigation during a transition, we skip + // the active transition, let it cleanup, then kick it off again here + React.useEffect(() => { + if (!vtContext.isTransitioning && interruption) { + setPendingState(interruption.state); + setVtContext({ + isTransitioning: true, + currentLocation: interruption.currentLocation, + nextLocation: interruption.nextLocation, + }); + setInterruption(undefined); + } + }, [vtContext.isTransitioning, interruption]); + + let navigator = React.useMemo((): Navigator => { + return { + createHref: router.createHref, + encodeLocation: router.encodeLocation, + go: (n) => router.navigate(n), + push: (to, state, opts) => + router.navigate(to, { + state, + preventScrollReset: opts?.preventScrollReset, + }), + replace: (to, state, opts) => + router.navigate(to, { + replace: true, + state, + preventScrollReset: opts?.preventScrollReset, + }), + }; + }, [router]); + + let basename = router.basename || "/"; + + let dataRouterContext = React.useMemo( + () => ({ + router, + navigator, + static: false, + basename, + }), + [router, navigator, basename] + ); + + // The fragment and {null} here are important! We need them to keep React 18's + // useId happy when we are server-rendering since we may have a