Skip to content

Commit

Permalink
fix: Wrap route with location arg in location context (#9094)
Browse files Browse the repository at this point in the history
* Wrap route with location arg in location context

In order for the `useLocation` hook to work with the use of the modal
pattern, routes with the locationArg prop are wrapped in a
LocationContext. This is done conditionally in order to ensure
performance in the default case doesn't change.

* Update useLocation unit tests

* Remove static markup from test for brevity

* Back out formatting change to test

Co-authored-by: John Pangalos <john@juni.co>
Co-authored-by: Matt Brophy <matt@brophy.org>
  • Loading branch information
3 people authored Sep 12, 2022
1 parent e13e1f9 commit e766ab5
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 7 deletions.
8 changes: 8 additions & 0 deletions .changeset/hungry-vans-ring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"react-router": patch
"react-router-dom": patch
"react-router-dom-v5-compat": patch
"react-router-native": patch
---

fix: update `useLocation` to return the scoped `Location` when inside a `<Routes location>` component
45 changes: 40 additions & 5 deletions packages/react-router/__tests__/useLocation-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import * as React from "react";
import * as TestRenderer from "react-test-renderer";
import { MemoryRouter, Routes, Route, useLocation } from "react-router";

function ShowPath() {
let { pathname, search, hash } = useLocation();
return <pre>{JSON.stringify({ pathname, search, hash })}</pre>;
function ShowLocation() {
let location = useLocation();
return <pre>{JSON.stringify(location)}</pre>;
}

describe("useLocation", () => {
Expand All @@ -14,16 +14,51 @@ describe("useLocation", () => {
renderer = TestRenderer.create(
<MemoryRouter initialEntries={["/home?the=search#the-hash"]}>
<Routes>
<Route path="/home" element={<ShowPath />} />
<Route path="/home" element={<ShowLocation />} />
</Routes>
</MemoryRouter>
);
});

expect(renderer.toJSON()).toMatchInlineSnapshot(`
<pre>
{"pathname":"/home","search":"?the=search","hash":"#the-hash"}
{"pathname":"/home","search":"?the=search","hash":"#the-hash","state":null,"key":"default"}
</pre>
`);
});

it("returns the scoped location object when nested in <Routes location>", () => {
let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
renderer = TestRenderer.create(
<MemoryRouter initialEntries={["/home?the=search#the-hash"]}>
<App />
</MemoryRouter>
);
});

function App() {
return (
<div>
<Routes>
<Route path="/home" element={<ShowLocation />} />
</Routes>
<Routes location="/scoped?scoped=search#scoped-hash">
<Route path="/scoped" element={<ShowLocation />} />
</Routes>
</div>
);
}

expect(renderer.toJSON()).toMatchInlineSnapshot(`
<div>
<pre>
{"pathname":"/home","search":"?the=search","hash":"#the-hash","state":null,"key":"default"}
</pre>
<pre>
{"pathname":"/scoped","search":"?scoped=search","hash":"#scoped-hash","state":null,"key":"default"}
</pre>
</div>
`);
});
});
29 changes: 27 additions & 2 deletions packages/react-router/lib/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import type {
PathPattern,
Router as RemixRouter,
To,
Action as NavigationType,
} from "@remix-run/router";
import {
Action as NavigationType,
invariant,
isRouteErrorResponse,
joinPaths,
Expand Down Expand Up @@ -425,7 +425,7 @@ export function useRoutes(
);
}

return _renderMatches(
let renderedMatches = _renderMatches(
matches &&
matches.map((match) =>
Object.assign({}, match, {
Expand All @@ -440,6 +440,31 @@ export function useRoutes(
parentMatches,
dataRouterStateContext || undefined
);

// When a user passes in a `locationArg`, the associated routes need to
// be wrapped in a new `LocationContext.Provider` in order for `useLocation`
// to use the scoped location instead of the global location.
if (locationArg) {
return (
<LocationContext.Provider
value={{
location: {
pathname: "/",
search: "",
hash: "",
state: null,
key: "default",
...location,
},
navigationType: NavigationType.Pop,
}}
>
{renderedMatches}
</LocationContext.Provider>
);
}

return renderedMatches;
}

function DefaultErrorElement() {
Expand Down

0 comments on commit e766ab5

Please sign in to comment.