Skip to content

Commit

Permalink
Revert "POC of TransitionProvider approach"
Browse files Browse the repository at this point in the history
This reverts commit 4cff152.
  • Loading branch information
brophdawg11 committed Oct 13, 2023
1 parent 4cff152 commit f2ddffc
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 83 deletions.
13 changes: 2 additions & 11 deletions examples/view-transitions/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
NavLink,
Outlet,
RouterProvider,
TransitionProvider,
unstable_useViewTransitionState,
useActionData,
useLoaderData,
Expand Down Expand Up @@ -247,21 +246,13 @@ function NavImage({ src, idx }: { src: string; idx: number }) {
const rootElement = document.getElementById("root") as HTMLElement;
ReactDOMClient.createRoot(rootElement).render(
<React.StrictMode>
<TransitionProvider
<RouterProvider
router={router}
future={{
// Wrap all state updates in React.startTransition()
v7_startTransition: true,
}}
>
<RouterProvider
router={router}
future={{
// Wrap all state updates in React.startTransition()
v7_startTransition: true,
}}
/>
</TransitionProvider>
/>
</React.StrictMode>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
useMatches,
useSearchParams,
createRoutesFromElements,
TransitionProvider,
} from "react-router-dom";

import createDeferred from "../../router/__tests__/utils/createDeferred";
Expand Down Expand Up @@ -6007,11 +6006,7 @@ function testDomRouter(
],
{ window: testWindow }
);
render(
<TransitionProvider router={router}>
<RouterProvider router={router} />
</TransitionProvider>
);
render(<RouterProvider router={router} />);

expect(screen.getByText("Home")).toBeDefined();
fireEvent.click(screen.getByText("/a"));
Expand Down
8 changes: 7 additions & 1 deletion packages/react-router-dom/__tests__/exports-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@ 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]);
Expand Down
97 changes: 80 additions & 17 deletions packages/react-router-dom/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -28,7 +31,7 @@ import {
UNSAFE_RouteContext as RouteContext,
UNSAFE_mapRouteProperties as mapRouteProperties,
UNSAFE_useRouteId as useRouteId,
UNSAFE_DataRouterSubscriberContext as DataRouterSubscriberContext,
UNSAFE_useRoutesImpl as useRoutesImpl,
} from "react-router";
import type {
BrowserHistory,
Expand Down Expand Up @@ -145,7 +148,6 @@ export {
Outlet,
Route,
Router,
RouterProvider,
Routes,
createMemoryRouter,
createPath,
Expand Down Expand Up @@ -417,20 +419,14 @@ class Deferred<T> {
}
}

interface TransitionProviderProps {
router: RemixRouter;
future?: Partial<FutureConfig>;
children: React.ReactNode | React.ReactNode[];
}

/**
* Enable support for View Transitions in a RouterProvider
* Given a Remix Router instance, render the appropriate UI
*/
export function TransitionProvider({
export function RouterProvider({
fallbackElement,
router,
future,
children,
}: TransitionProviderProps): React.ReactElement {
}: RouterProviderProps): React.ReactElement {
let [state, setStateImpl] = React.useState(router.state);
let [pendingState, setPendingState] = React.useState<RouterState>();
let [vtContext, setVtContext] = React.useState<ViewTransitionContextObject>({
Expand Down Expand Up @@ -491,6 +487,10 @@ export function TransitionProvider({
[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/<Navigate>)
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(() => {
Expand Down Expand Up @@ -546,15 +546,78 @@ export function TransitionProvider({
}
}, [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 <script> here
// containing the hydrated server-side staticContext (from StaticRouterProvider).
// useId relies on the component tree structure to generate deterministic id's
// so we need to ensure it remains the same on the client even though
// we don't need the <script> tag
return (
<DataRouterSubscriberContext.Provider value={[state, setState]}>
<ViewTransitionContext.Provider value={vtContext}>
{children}
</ViewTransitionContext.Provider>
</DataRouterSubscriberContext.Provider>
<>
<DataRouterContext.Provider value={dataRouterContext}>
<DataRouterStateContext.Provider value={state}>
<ViewTransitionContext.Provider value={vtContext}>
<Router
basename={basename}
location={state.location}
navigationType={state.historyAction}
navigator={navigator}
>
{state.initialized ? (
<DataRoutes routes={router.routes} state={state} />
) : (
fallbackElement
)}
</Router>
</ViewTransitionContext.Provider>
</DataRouterStateContext.Provider>
</DataRouterContext.Provider>
{null}
</>
);
}

function DataRoutes({
routes,
state,
}: {
routes: DataRouteObject[];
state: RouterState;
}): React.ReactElement | null {
return useRoutesImpl(routes, undefined, state);
}

export interface BrowserRouterProps {
basename?: string;
children?: React.ReactNode;
Expand Down
33 changes: 15 additions & 18 deletions packages/react-router-dom/server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
UNSAFE_convertRoutesToDataRoutes as convertRoutesToDataRoutes,
} from "@remix-run/router";
import {
UNSAFE_DataRouterSubscriberContext as DataRouterSubscriberContext,
UNSAFE_mapRouteProperties as mapRouteProperties,
UNSAFE_useRoutesImpl as useRoutesImpl,
} from "react-router";
Expand Down Expand Up @@ -131,23 +130,21 @@ export function StaticRouterProvider({

return (
<>
<DataRouterSubscriberContext.Provider value={null}>
<ViewTransitionContext.Provider value={{ isTransitioning: false }}>
<DataRouterContext.Provider value={dataRouterContext}>
<DataRouterStateContext.Provider value={state}>
<Router
basename={dataRouterContext.basename}
location={state.location}
navigationType={state.historyAction}
navigator={dataRouterContext.navigator}
static={dataRouterContext.static}
>
<DataRoutes routes={router.routes} state={state} />
</Router>
</DataRouterStateContext.Provider>
</DataRouterContext.Provider>
</ViewTransitionContext.Provider>
</DataRouterSubscriberContext.Provider>
<DataRouterContext.Provider value={dataRouterContext}>
<DataRouterStateContext.Provider value={state}>
<ViewTransitionContext.Provider value={{ isTransitioning: false }}>
<Router
basename={dataRouterContext.basename}
location={state.location}
navigationType={state.historyAction}
navigator={dataRouterContext.navigator}
static={dataRouterContext.static}
>
<DataRoutes routes={router.routes} state={state} />
</Router>
</ViewTransitionContext.Provider>
</DataRouterStateContext.Provider>
</DataRouterContext.Provider>
{hydrateScript ? (
<script
suppressHydrationWarning
Expand Down
1 change: 0 additions & 1 deletion packages/react-router-native/__tests__/exports-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import * as ReactRouterNative from "react-router-native";
let nonReExportedKeys = new Set([
"UNSAFE_mapRouteProperties",
"UNSAFE_useRoutesImpl",
"UNSAFE_DataRouterSubscriberContext",
"UNSAFE_ViewTransitionContext",
]);

Expand Down
2 changes: 0 additions & 2 deletions packages/react-router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ import type {
import {
DataRouterContext,
DataRouterStateContext,
DataRouterSubscriberContext,
LocationContext,
NavigationContext,
RouteContext,
Expand Down Expand Up @@ -306,7 +305,6 @@ export function createMemoryRouter(
export {
DataRouterContext as UNSAFE_DataRouterContext,
DataRouterStateContext as UNSAFE_DataRouterStateContext,
DataRouterSubscriberContext as UNSAFE_DataRouterSubscriberContext,
LocationContext as UNSAFE_LocationContext,
NavigationContext as UNSAFE_NavigationContext,
RouteContext as UNSAFE_RouteContext,
Expand Down
23 changes: 5 additions & 18 deletions packages/react-router/lib/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import {
AwaitContext,
DataRouterContext,
DataRouterStateContext,
DataRouterSubscriberContext,
LocationContext,
NavigationContext,
RouteContext,
Expand Down Expand Up @@ -93,35 +92,23 @@ export function RouterProvider({
router,
future,
}: RouterProviderProps): React.ReactElement {
let ctx = React.useContext(DataRouterSubscriberContext);
let ctxState = ctx ? ctx[0] : null;
let ctxSetState = ctx ? ctx[1] : null;
let [localState, localSetStateImpl] = React.useState(router.state);
let [state, setStateImpl] = React.useState(router.state);
let { v7_startTransition } = future || {};

let setState = React.useCallback<RouterSubscriber>(
(newState: RouterState) => {
if (v7_startTransition && startTransitionImpl) {
startTransitionImpl(() => localSetStateImpl(newState));
startTransitionImpl(() => setStateImpl(newState));
} else {
localSetStateImpl(newState);
setStateImpl(newState);
}
},
[localSetStateImpl, v7_startTransition]
[setStateImpl, v7_startTransition]
);

// If we're inside a DataRouterSubscriberContext that needs fine-grained
// control of the state updates (for startViewTransition), then prefer those
// over our own state/setStateImpl
let state = ctxState || localState;
let subscriber = ctxSetState || setState;

// Need to use a layout effect here so we are subscribed early enough to
// pick up on any render-driven redirects/navigations (useEffect/<Navigate>)
React.useLayoutEffect(
() => router.subscribe(subscriber),
[router, subscriber]
);
React.useLayoutEffect(() => router.subscribe(setState), [router, setState]);

let navigator = React.useMemo((): Navigator => {
return {
Expand Down
9 changes: 0 additions & 9 deletions packages/react-router/lib/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import type {
Action as NavigationType,
RelativeRoutingType,
Router,
RouterState,
RouterSubscriber,
StaticHandlerContext,
To,
TrackedPromise,
Expand Down Expand Up @@ -86,13 +84,6 @@ if (__DEV__) {
DataRouterStateContext.displayName = "DataRouterState";
}

export const DataRouterSubscriberContext = React.createContext<
[RouterState, RouterSubscriber] | null
>(null);
if (__DEV__) {
DataRouterSubscriberContext.displayName = "DataRouterSubscriber";
}

export const AwaitContext = React.createContext<TrackedPromise | null>(null);
if (__DEV__) {
AwaitContext.displayName = "Await";
Expand Down

0 comments on commit f2ddffc

Please sign in to comment.