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(`
+ ""
+ `);
+
+ 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(`
+ ""
+ `);
+
+ 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 (
-
-
+
+
);
}