Skip to content

Commit da65120

Browse files
authored
Fix useMatch undecoded params (#11789)
1 parent 03d6cf2 commit da65120

File tree

5 files changed

+56
-2
lines changed

5 files changed

+56
-2
lines changed

.changeset/big-trainers-cough.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Fix regression and properly decode paths inside `useMatch` so matches/params reflect decoded params

packages/react-router-dom/__tests__/special-characters-test.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
HashRouter,
1616
MemoryRouter,
1717
Link,
18+
Outlet,
1819
Routes,
1920
Route,
2021
RouterProvider,
@@ -23,6 +24,7 @@ import {
2324
createMemoryRouter,
2425
createRoutesFromElements,
2526
useLocation,
27+
useMatch,
2628
useNavigate,
2729
useParams,
2830
} from "react-router-dom";
@@ -962,6 +964,51 @@ describe("special character tests", () => {
962964
`"<pre>{"pathname":"/with%20space","search":"","hash":""}</pre>"`
963965
);
964966
});
967+
968+
it("properly decodes params in useMatch", () => {
969+
let testWindow = getWindow("/user/bücherwurm");
970+
971+
let router = createBrowserRouter(
972+
[
973+
{
974+
path: "/",
975+
Component() {
976+
let match = useMatch("/user/:username");
977+
return (
978+
<>
979+
<pre>{JSON.stringify(match, null, 2)}</pre>
980+
<Outlet />
981+
</>
982+
);
983+
},
984+
children: [
985+
{
986+
path: "user/:username",
987+
element: null,
988+
},
989+
],
990+
},
991+
],
992+
{ window: testWindow }
993+
);
994+
let ctx = render(<RouterProvider router={router} />);
995+
996+
expect(testWindow.location.pathname).toBe("/user/b%C3%BCcherwurm");
997+
expect(ctx.container.innerHTML).toMatchInlineSnapshot(`
998+
"<pre>{
999+
"params": {
1000+
"username": "bücherwurm"
1001+
},
1002+
"pathname": "/user/bücherwurm",
1003+
"pathnameBase": "/user/bücherwurm",
1004+
"pattern": {
1005+
"path": "/user/:username",
1006+
"caseSensitive": false,
1007+
"end": true
1008+
}
1009+
}</pre>"
1010+
`);
1011+
});
9651012
});
9661013

9671014
describe("hash routers", () => {

packages/react-router/lib/hooks.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
IDLE_BLOCKER,
1919
Action as NavigationType,
2020
UNSAFE_convertRouteMatchToUiMatch as convertRouteMatchToUiMatch,
21+
UNSAFE_decodePath as decodePath,
2122
UNSAFE_getResolveToMatches as getResolveToMatches,
2223
UNSAFE_invariant as invariant,
2324
isRouteErrorResponse,
@@ -141,7 +142,7 @@ export function useMatch<
141142

142143
let { pathname } = useLocation();
143144
return React.useMemo(
144-
() => matchPath<ParamKey, Path>(pattern, pathname),
145+
() => matchPath<ParamKey, Path>(pattern, decodePath(pathname)),
145146
[pathname, pattern]
146147
);
147148
}

packages/router/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export {
9292
ErrorResponseImpl as UNSAFE_ErrorResponseImpl,
9393
convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes,
9494
convertRouteMatchToUiMatch as UNSAFE_convertRouteMatchToUiMatch,
95+
decodePath as UNSAFE_decodePath,
9596
getResolveToMatches as UNSAFE_getResolveToMatches,
9697
} from "./utils";
9798

packages/router/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ function compilePath(
10751075
return [matcher, params];
10761076
}
10771077

1078-
function decodePath(value: string) {
1078+
export function decodePath(value: string) {
10791079
try {
10801080
return value
10811081
.split("/")

0 commit comments

Comments
 (0)