I'm trying to understand how useParams works in React Router. I have two routes—one with a dynamic parameter and another with a wildcard (*).
What should i do to get the
// {
// "appId": "appid",
// "*": "page2"
// }
I have two routes:
/appid/page2
/appid/page3
Here’s my setup: https://codesandbox.io/p/sandbox/react-router-v6-forked-sw5ndw?file=%2Fsrc%2FApp.js%3A1%2C1-69%2C1
import React from "react";
import {
BrowserRouter,
Routes,
Route,
Navigate,
Link,
useParams,
} from "react-router-dom";
import "./styles.css";
function HomePage() {
return <div className="page">🏠 Page</div>;
}
function Page1() {
return <div className="page">🏠 Page1</div>;
}
function Page2() {
console.log(useParams());
// {
// "appId": "appid",
// "*": ""
// }
return <div className="page">🏠 Page2</div>;
}
function Page3() {
console.log(useParams());
// {
// "appId": "appid",
// "*": "page3"
// }
return <div className="page">🏠 Page3</div>;
}
function Application() {
return (
<>
1
<Routes>
<Route path="/" element={<Page1 />} />
<Route path="/page2/*" element={<Page2 />} />
<Route path="/page3" element={<Page3 />} />
</Routes>
</>
);
}
export default function App() {
return (
<BrowserRouter>
<div>
<Link to="/" className="link">
Home
</Link>
<Link to="/appid/page2" className="link">
APP Page 2
</Link>
<Link to="/appid/page3" className="link">
APP Page 2
</Link>
</div>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path=":appId/*" element={<Application />} />
</Routes>
</BrowserRouter>
);
}
The main issue here is that when the route path is "/appid/page2"
is that the splat "*"
param is first set to "page2"
from the root ":appId/*"
as you are expecting, but then Page2
is rendering additional descendent routes that overwrites the splat "*"
parameter value with its own value, which is ""
in this case because there are no further path segments to read.
If you want to see the Application
component's "view" of the route params then use the useParams
hook in Application
instead of a more deeply nested route component.
function Application() {
console.log(useParams()); // { appId: 'appid', *: 'page2' }
return (
<>
1
<Routes>
<Route path="/" element={<Page1 />} />
<Route path="/page2/*" element={<Page2 />} />
<Route path="/page3" element={<Page3 />} />
</Routes>
</>
);
}
Page2
will see its "view" of the params, which is { appId: 'appid', *: '' }
as you have observed.
If the intention is for Page2
to also render further descendent routes then I'd suggest giving each segment through the path a unique path parameter identifier, e.g. path="/:page2/*"
.
Example:
App
<Routes>
<Route path="/" element={<HomePage />} />
<Route path=":appId/*" element={<Application />} />
</Routes>
Application
<Routes>
<Route path="/" element={<Page1 />} />
<Route path="/:page2/*" element={<Page2 />} />
<Route path="/page3" element={<Page3 />} />
</Routes>
Now when the route path is "/appid/page2"
the result of useParams()
will be:
{
*: "",
appId: "appid",
page2: "page2",
}
When you are on a Page2 sub-route like "/appid/page2/foo"
, the result will be:
{
*: "foo",
appId: "appid",
page2: "page2",
}