Skip to content

Commit

Permalink
Fix basename duplication in RouterProvider descendant routes (#10492)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 committed May 16, 2023
1 parent 55482ec commit e665a46
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/fix-basename-duplication.md
@@ -0,0 +1,5 @@
---
"react-router": patch
---

Fix `basename` duplication in descenant `<Routes>` inside a `<RouterProvider>`
175 changes: 175 additions & 0 deletions packages/react-router/__tests__/useNavigate-test.tsx
Expand Up @@ -2053,6 +2053,181 @@ describe("useNavigate", () => {
`);
});
});

describe("with a basename", () => {
describe("in a MemoryRouter", () => {
it("in a root route", () => {
let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
renderer = TestRenderer.create(
<MemoryRouter basename="/base" initialEntries={["/base"]}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/path" element={<h1>Path</h1>} />
</Routes>
</MemoryRouter>
);
});

function Home() {
let navigate = useNavigate();
return <button onClick={() => navigate("/path")} />;
}

// @ts-expect-error
expect(renderer.toJSON()).toMatchInlineSnapshot(`
<button
onClick={[Function]}
/>
`);

// @ts-expect-error
let button = renderer.root.findByType("button");
TestRenderer.act(() => button.props.onClick());

// @ts-expect-error
expect(renderer.toJSON()).toMatchInlineSnapshot(`
<h1>
Path
</h1>
`);
});

it("in a descendant route", () => {
let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
renderer = TestRenderer.create(
<MemoryRouter basename="/base" initialEntries={["/base"]}>
<Routes>
<Route
path="/*"
element={
<Routes>
<Route index element={<Home />} />
</Routes>
}
/>
<Route path="/path" element={<h1>Path</h1>} />
</Routes>
</MemoryRouter>
);
});

function Home() {
let navigate = useNavigate();
return <button onClick={() => navigate("/path")} />;
}

// @ts-expect-error
expect(renderer.toJSON()).toMatchInlineSnapshot(`
<button
onClick={[Function]}
/>
`);

// @ts-expect-error
let button = renderer.root.findByType("button");
TestRenderer.act(() => button.props.onClick());

// @ts-expect-error
expect(renderer.toJSON()).toMatchInlineSnapshot(`
<h1>
Path
</h1>
`);
});
});

describe("in a RouterProvider", () => {
it("in a root route", () => {
let router = createMemoryRouter(
[
{
path: "/",
Component: Home,
},
{ path: "/path", Component: () => <h1>Path</h1> },
],
{ basename: "/base", initialEntries: ["/base"] }
);

function Home() {
let navigate = useNavigate();
return <button onClick={() => navigate("/path")} />;
}

let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
renderer = TestRenderer.create(<RouterProvider router={router} />);
});

// @ts-expect-error
expect(renderer.toJSON()).toMatchInlineSnapshot(`
<button
onClick={[Function]}
/>
`);

// @ts-expect-error
let button = renderer.root.findByType("button");
TestRenderer.act(() => button.props.onClick());

// @ts-expect-error
expect(renderer.toJSON()).toMatchInlineSnapshot(`
<h1>
Path
</h1>
`);
});

it("in a descendant route", () => {
let router = createMemoryRouter(
[
{
path: "/*",
Component() {
return (
<Routes>
<Route index element={<Home />} />
</Routes>
);
},
},
{ path: "/path", Component: () => <h1>Path</h1> },
],
{ basename: "/base", initialEntries: ["/base"] }
);

function Home() {
let navigate = useNavigate();
return <button onClick={() => navigate("/path")} />;
}

let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
renderer = TestRenderer.create(<RouterProvider router={router} />);
});

// @ts-expect-error
expect(renderer.toJSON()).toMatchInlineSnapshot(`
<button
onClick={[Function]}
/>
`);

// @ts-expect-error
let button = renderer.root.findByType("button");
TestRenderer.act(() => button.props.onClick());

// @ts-expect-error
expect(renderer.toJSON()).toMatchInlineSnapshot(`
<h1>
Path
</h1>
`);
});
});
});
});

function UseNavigateButton({
Expand Down
19 changes: 14 additions & 5 deletions packages/react-router/lib/hooks.tsx
Expand Up @@ -188,6 +188,7 @@ function useNavigateUnstable(): NavigateFunction {
`useNavigate() may be used only in the context of a <Router> component.`
);

let dataRouterContext = React.useContext(DataRouterContext);
let { basename, navigator } = React.useContext(NavigationContext);
let { matches } = React.useContext(RouteContext);
let { pathname: locationPathname } = useLocation();
Expand Down Expand Up @@ -222,10 +223,12 @@ function useNavigateUnstable(): NavigateFunction {
);

// If we're operating within a basename, prepend it to the pathname prior
// to handing off to history. If this is a root navigation, then we
// navigate to the raw basename which allows the basename to have full
// control over the presence of a trailing slash on root links
if (basename !== "/") {
// to handing off to history (but only if we're not in a data router,
// otherwise it'll prepend the basename inside of the router).
// If this is a root navigation, then we navigate to the raw basename
// which allows the basename to have full control over the presence of a
// trailing slash on root links
if (dataRouterContext == null && basename !== "/") {
path.pathname =
path.pathname === "/"
? basename
Expand All @@ -238,7 +241,13 @@ function useNavigateUnstable(): NavigateFunction {
options
);
},
[basename, navigator, routePathnamesJson, locationPathname]
[
basename,
navigator,
routePathnamesJson,
locationPathname,
dataRouterContext,
]
);

return navigate;
Expand Down

0 comments on commit e665a46

Please sign in to comment.