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 005d44feeb..7ec48fca3f 100644 --- a/packages/react-router-dom/__tests__/data-browser-router-test.tsx +++ b/packages/react-router-dom/__tests__/data-browser-router-test.tsx @@ -31,6 +31,7 @@ import { useFetchers, UNSAFE_DataRouterStateContext as DataRouterStateContext, defer, + useLocation, } from "react-router-dom"; // Private API @@ -330,6 +331,54 @@ function testDomRouter( `); }); + it("renders fallbackElement within router contexts", async () => { + let fooDefer = createDeferred(); + let { container } = render( + } + > + }> + fooDefer.promise} + element={} + /> + + + ); + + function FallbackElement() { + let location = useLocation(); + return

Loading{location.pathname}

; + } + + function Foo() { + let data = useLoaderData(); + return

Foo:{data?.message}

; + } + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
+

+ Loading + /foo +

+
" + `); + + fooDefer.resolve({ message: "From Foo Loader" }); + await waitFor(() => screen.getByText("Foo:From Foo Loader")); + expect(getHtml(container)).toMatchInlineSnapshot(` + "
+

+ Foo: + From Foo Loader +

+
" + `); + }); + it("handles link navigations", async () => { render( diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx index fb36ddd67f..8a05c3889c 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -236,12 +236,8 @@ export function DataBrowserRouter({ let router = routerSingleton; return ( - - + + ); } @@ -276,12 +272,8 @@ export function DataHashRouter({ let router = routerSingleton; return ( - - + + ); } diff --git a/packages/react-router/__tests__/data-memory-router-test.tsx b/packages/react-router/__tests__/data-memory-router-test.tsx index 4f322ff233..1294fd9af9 100644 --- a/packages/react-router/__tests__/data-memory-router-test.tsx +++ b/packages/react-router/__tests__/data-memory-router-test.tsx @@ -11,11 +11,7 @@ import "@testing-library/jest-dom"; import type { FormMethod, Router } from "@remix-run/router"; import { createMemoryRouter } from "@remix-run/router"; -import { - DataMemoryRouterProps, - UNSAFE_DataRouter as DataRouter, - UNSAFE_DataRouterProvider as DataRouterProvider, -} from "react-router"; +import type { DataMemoryRouterProps } from "react-router"; import { DataMemoryRouter, Await, @@ -26,6 +22,7 @@ import { useAsyncError, useAsyncValue, useLoaderData, + useLocation, useMatches, useRouteLoaderData, useRouteError, @@ -33,7 +30,9 @@ import { useRevalidator, MemoryRouter, Routes, + UNSAFE_DataRouter as DataRouter, UNSAFE_DataRouterContext as DataRouterContext, + UNSAFE_DataRouterProvider as DataRouterProvider, } from "react-router"; // Private API @@ -301,6 +300,54 @@ describe("", () => { `); }); + it("renders fallbackElement within router contexts", async () => { + let fooDefer = createDeferred(); + let { container } = render( + } + initialEntries={["/foo"]} + > + }> + fooDefer.promise} element={} /> + + + ); + + function FallbackElement() { + let location = useLocation(); + return ( + <> +

Loading{location.pathname}

+ + ); + } + + function Foo() { + let data = useLoaderData(); + return

Foo:{data?.message}

; + } + + expect(getHtml(container)).toMatchInlineSnapshot(` + "
+

+ Loading + /foo +

+
" + `); + + fooDefer.resolve({ message: "From Foo Loader" }); + await waitFor(() => screen.getByText("Foo:From Foo Loader")); + expect(getHtml(container)).toMatchInlineSnapshot(` + "
+

+ Foo: + From Foo Loader +

+
" + `); + }); + it("handles link navigations", async () => { render( diff --git a/packages/react-router/lib/components.tsx b/packages/react-router/lib/components.tsx index 238faebe2e..cfcf587cf3 100644 --- a/packages/react-router/lib/components.tsx +++ b/packages/react-router/lib/components.tsx @@ -58,6 +58,12 @@ export function _resetModuleScope() { routerSingleton = null; } +interface DataRouterProviderProps { + basename?: string; + children?: React.ReactNode; + router: RemixRouter; +} + /** * A higher-order component that, given a Remix Router instance. setups the * Context's required for data routing @@ -65,14 +71,8 @@ export function _resetModuleScope() { export function DataRouterProvider({ basename, children, - fallbackElement, router, -}: { - basename?: string; - children?: React.ReactNode; - fallbackElement?: React.ReactNode; - router: RemixRouter; -}): React.ReactElement { +}: DataRouterProviderProps): React.ReactElement { // Sync router state to our component state to force re-renders let state: RouterState = useSyncExternalStoreShim( router.subscribe, @@ -98,29 +98,29 @@ export function DataRouterProvider({ }; }, [router]); - let dataRouterContext: DataRouterContextObject = { - router, - navigator, - static: false, - basename: basename || "/", - }; - - if (!state.initialized) { - return <>{fallbackElement}; - } - return ( - + ); } +interface DataRouterProps { + fallbackElement?: React.ReactNode; +} + /** * A data-aware wrapper for `` that leverages the Context's provided by * `` */ -export function DataRouter() { +export function DataRouter({ fallbackElement }: DataRouterProps) { let dataRouterContext = React.useContext(DataRouterContext); invariant( dataRouterContext, @@ -135,7 +135,7 @@ export function DataRouter() { navigationType={router.state.historyAction} navigator={navigator} > - + {router.state.initialized ? : fallbackElement} ); } @@ -173,12 +173,8 @@ export function DataMemoryRouter({ let router = routerSingleton; return ( - - + + ); }