From 4cff152a3891d4fe67edfc6457693ec323fe2fd4 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Fri, 13 Oct 2023 15:29:14 -0400 Subject: [PATCH] POC of TransitionProvider approach --- examples/view-transitions/src/main.tsx | 13 ++- .../__tests__/data-browser-router-test.tsx | 7 +- .../__tests__/exports-test.tsx | 8 +- packages/react-router-dom/index.tsx | 97 ++++--------------- packages/react-router-dom/server.tsx | 33 ++++--- .../__tests__/exports-test.tsx | 1 + packages/react-router/index.ts | 2 + packages/react-router/lib/components.tsx | 23 ++++- packages/react-router/lib/context.ts | 9 ++ 9 files changed, 83 insertions(+), 110 deletions(-) diff --git a/examples/view-transitions/src/main.tsx b/examples/view-transitions/src/main.tsx index bfe23d1fc0..16f1f2103a 100644 --- a/examples/view-transitions/src/main.tsx +++ b/examples/view-transitions/src/main.tsx @@ -10,6 +10,7 @@ import { NavLink, Outlet, RouterProvider, + TransitionProvider, unstable_useViewTransitionState, useActionData, useLoaderData, @@ -246,13 +247,21 @@ function NavImage({ src, idx }: { src: string; idx: number }) { const rootElement = document.getElementById("root") as HTMLElement; ReactDOMClient.createRoot(rootElement).render( - + > + + ); diff --git a/packages/react-router-dom/__tests__/data-browser-router-test.tsx b/packages/react-router-dom/__tests__/data-browser-router-test.tsx index aca4d565f9..ff24b27756 100644 --- a/packages/react-router-dom/__tests__/data-browser-router-test.tsx +++ b/packages/react-router-dom/__tests__/data-browser-router-test.tsx @@ -34,6 +34,7 @@ import { useMatches, useSearchParams, createRoutesFromElements, + TransitionProvider, } from "react-router-dom"; import createDeferred from "../../router/__tests__/utils/createDeferred"; @@ -6006,7 +6007,11 @@ function testDomRouter( ], { window: testWindow } ); - render(); + render( + + + + ); expect(screen.getByText("Home")).toBeDefined(); fireEvent.click(screen.getByText("/a")); diff --git a/packages/react-router-dom/__tests__/exports-test.tsx b/packages/react-router-dom/__tests__/exports-test.tsx index dcf2e22ba7..2d94b97884 100644 --- a/packages/react-router-dom/__tests__/exports-test.tsx +++ b/packages/react-router-dom/__tests__/exports-test.tsx @@ -4,21 +4,15 @@ import * as ReactRouterDOM from "react-router-dom"; let nonReExportedKeys = new Set([ "UNSAFE_mapRouteProperties", "UNSAFE_useRoutesImpl", + "UNSAFE_DataRouterSubscriberContext", ]); -let modifiedExports = new Set(["RouterProvider"]); - describe("react-router-dom", () => { for (let key in ReactRouter) { 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 8c62c56893..68d958d5e9 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -4,15 +4,12 @@ */ import * as React from "react"; import type { - DataRouteObject, FutureConfig, Location, NavigateOptions, NavigationType, - Navigator, RelativeRoutingType, RouteObject, - RouterProviderProps, To, } from "react-router"; import { @@ -31,7 +28,7 @@ import { UNSAFE_RouteContext as RouteContext, UNSAFE_mapRouteProperties as mapRouteProperties, UNSAFE_useRouteId as useRouteId, - UNSAFE_useRoutesImpl as useRoutesImpl, + UNSAFE_DataRouterSubscriberContext as DataRouterSubscriberContext, } from "react-router"; import type { BrowserHistory, @@ -148,6 +145,7 @@ export { Outlet, Route, Router, + RouterProvider, Routes, createMemoryRouter, createPath, @@ -419,14 +417,20 @@ class Deferred { } } +interface TransitionProviderProps { + router: RemixRouter; + future?: Partial; + children: React.ReactNode | React.ReactNode[]; +} + /** - * Given a Remix Router instance, render the appropriate UI + * Enable support for View Transitions in a RouterProvider */ -export function RouterProvider({ - fallbackElement, +export function TransitionProvider({ router, future, -}: RouterProviderProps): React.ReactElement { + children, +}: TransitionProviderProps): React.ReactElement { let [state, setStateImpl] = React.useState(router.state); let [pendingState, setPendingState] = React.useState(); let [vtContext, setVtContext] = React.useState({ @@ -487,10 +491,6 @@ export function RouterProvider({ [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(() => { @@ -546,78 +546,15 @@ export function RouterProvider({ } }, [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