In v5, we could add trailing ? to route for optional parameters, but as in v6, the support for the same has been dropped, so what's the alternate way of writing the following piece of code?
<Route path="/cart/:id?" component={<CartPage />} />
react-router-dom#6.5.0+
Optional parameters have been re-introduced to the library. The docs haven't been updated from what I can tell, but v6.5.0 Release Notes include the details.
The above routes can be merged to a single route:
<Route path="/cart/:id?" element={<CartPage />} />
react-router-dom#6-6.4.5
After quite a bit of digging through the source code to understand how path parsing was different in RRDv6 from RRDv5, and turned up nothing really other than they no longer use path-to-regex, I hit up the repo's issues section and found this issue which outright states they don't plan to support optional path parameters in v6.
See FAQ: What Happened to Regexp Routes Paths?
It seems the suggested solution is to render 2 routes to match either path and render the same component.
Example:
<Route path="/cart/:id" element={<CartPage />} />
<Route path="/cart/" element={<CartPage />} />
or
<Route path="/cart">
<Route index element={<CartPage />} />
<Route path=":id" element={<CartPage />} />
</Route>
The latter is really only syntactic sugar around relative paths from "/cart" though.
As Drew Reese said, there is no support for the optional parameters in v6 (at least as of now).
I have ended up writing these little helper functions that register all nested routes for the optional parameters.
const registerOptionalParamRoute = (optionalParams: string[], element: Element) => {
if (optionalParams.length === 0)
return <Fragment/>;
const param = optionalParams[0];
optionalParams.splice(0, 1);
return <Route path={param} element={element}>
{registerOptionalParamRoute(optionalParams, element)}
</Route>;
};
const registerOptionalParams = (path: string, element: JSX.Element) => {
const params = path.split("/");
let basePath = "";
let optionalParams = [];
for (let i = 0; i < params.length; i++) {
if (params[i] === '')
continue;
if (!params[i].includes("?"))
basePath += "/" + params[i];
else
optionalParams.push(params[i].substr(0, params[i].length - 1));
}
return <Route path={basePath} key={basePath} element={element}>
{registerOptionalParamRoute(optionalParams, element)}
</Route>;
};
Then call it:
<Routes>
{registerOptionalParams('/component/:param1?/:param2?', <Component/>)}
</Routes>
For an example url /component/:param1?/:param2? and given component <Component/> it generates the following jsx element:
<Route path="component" element={<Component/>}>
<Route path=":param1" element={<Component/>}>
<Route path=":param2" element={<Component/>} />
</Route>
</Route>
I have also created feature request for optional parameters (https://github.com/remix-run/react-router/issues/8381), will see what feedback it will get.
Use wildcard:
<Route path="/cart/*" component={<CartPage />} />
It would be great if this could be described in the migration guide – or if there was at least a hint. So the recommended way of doing "optional" params really is ...
<Route path='/page/:friendlyName/:sort' element={<Page/>} />
<Route path='/page/:friendlyName/' element={<Page/>} />
Base react router doc
https://reactrouter.com/docs/en/v6/hooks/use-location
would not this work?
import * as React from 'react';
import { useLocation } from 'react-router-dom';
function App() {
let location = useLocation();
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
return (
// ...
);
}
import * as React from 'react';
import {useNavigate} from "react-router-dom"
function App() {
const location = useNavigate();
React.useEffect(() => {
ga('send', 'pageview');
location("/route");
}, [location]);
return (
// ...
);
}
this is not working because it is not supported in version 6 in react router dom
you must use
import {useNavigate} from "react-router-dom"
Related
I am simply trying to retrieve the params from a dynamic route ("/articles/:articleId") in React using react-router-dom v 6.8
"react-router-dom": "^6.8.0",
"#types/react-router-dom": "^5.3.3",
I tried to follow the Docs, but I must be missing something here.
App.tsx
function App() {
return (
<Router>
<Routes>
<Route path={"/articles/:articleId"} element={Article()}/>
<Route path={"/articles"} element={Articles()}/>
<Route path={"/404"} element={NotFoundPage()}/>
<Route path={"/"} element={HomePage()}/>
<Route path={"*"} element={NotFoundPage()}/>
</Routes>
</Router>
);
}
Article.tsx
import { useParams } from "react-router-dom";
import { useEffect } from "react";
const Article = () => {
const { articleId } = useParams()
const params = useParams()
useEffect(() => {
console.log('even inside useEffect', params);
}, [])
console.log("Article ", useParams(), articleId)
return (
<div>ID: {articleId}</div>
)
}
export default Article
Output
Couple of issues:
You are directly calling the React functions instead of rendering them as JSX. The components aren't called as part of the normal React component lifecycle, so the params are not populated.
react-router-dom#6 is written entirely in Typescript, so there is no need for any external typings, especially those from react-router-dom#5 which will be woefully incorrect for RRDv6.
The Route component's element prop takes a React.ReactNode, a.k.a. JSX, value.
function App() {
return (
<Router>
<Routes>
<Route path="/articles/:articleId" element={<Article />} />
<Route path="/articles" element={<Articles />} />
<Route path="/404" element={<NotFoundPage />} />
<Route path="/" element={<HomePage />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</Router>
);
}
Uninstall the unnecessary dependency.
npm uninstall --save #types/react-router-dom
In v5, we could add trailing ? to route for optional parameters, but as in v6, the support for the same has been dropped, so what's the alternate way of writing the following piece of code?
<Route path="/cart/:id?" component={<CartPage />} />
react-router-dom#6.5.0+
Optional parameters have been re-introduced to the library. The docs haven't been updated from what I can tell, but v6.5.0 Release Notes include the details.
The above routes can be merged to a single route:
<Route path="/cart/:id?" element={<CartPage />} />
react-router-dom#6-6.4.5
After quite a bit of digging through the source code to understand how path parsing was different in RRDv6 from RRDv5, and turned up nothing really other than they no longer use path-to-regex, I hit up the repo's issues section and found this issue which outright states they don't plan to support optional path parameters in v6.
See FAQ: What Happened to Regexp Routes Paths?
It seems the suggested solution is to render 2 routes to match either path and render the same component.
Example:
<Route path="/cart/:id" element={<CartPage />} />
<Route path="/cart/" element={<CartPage />} />
or
<Route path="/cart">
<Route index element={<CartPage />} />
<Route path=":id" element={<CartPage />} />
</Route>
The latter is really only syntactic sugar around relative paths from "/cart" though.
As Drew Reese said, there is no support for the optional parameters in v6 (at least as of now).
I have ended up writing these little helper functions that register all nested routes for the optional parameters.
const registerOptionalParamRoute = (optionalParams: string[], element: Element) => {
if (optionalParams.length === 0)
return <Fragment/>;
const param = optionalParams[0];
optionalParams.splice(0, 1);
return <Route path={param} element={element}>
{registerOptionalParamRoute(optionalParams, element)}
</Route>;
};
const registerOptionalParams = (path: string, element: JSX.Element) => {
const params = path.split("/");
let basePath = "";
let optionalParams = [];
for (let i = 0; i < params.length; i++) {
if (params[i] === '')
continue;
if (!params[i].includes("?"))
basePath += "/" + params[i];
else
optionalParams.push(params[i].substr(0, params[i].length - 1));
}
return <Route path={basePath} key={basePath} element={element}>
{registerOptionalParamRoute(optionalParams, element)}
</Route>;
};
Then call it:
<Routes>
{registerOptionalParams('/component/:param1?/:param2?', <Component/>)}
</Routes>
For an example url /component/:param1?/:param2? and given component <Component/> it generates the following jsx element:
<Route path="component" element={<Component/>}>
<Route path=":param1" element={<Component/>}>
<Route path=":param2" element={<Component/>} />
</Route>
</Route>
I have also created feature request for optional parameters (https://github.com/remix-run/react-router/issues/8381), will see what feedback it will get.
Use wildcard:
<Route path="/cart/*" component={<CartPage />} />
It would be great if this could be described in the migration guide – or if there was at least a hint. So the recommended way of doing "optional" params really is ...
<Route path='/page/:friendlyName/:sort' element={<Page/>} />
<Route path='/page/:friendlyName/' element={<Page/>} />
Base react router doc
https://reactrouter.com/docs/en/v6/hooks/use-location
would not this work?
import * as React from 'react';
import { useLocation } from 'react-router-dom';
function App() {
let location = useLocation();
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
return (
// ...
);
}
import * as React from 'react';
import {useNavigate} from "react-router-dom"
function App() {
const location = useNavigate();
React.useEffect(() => {
ga('send', 'pageview');
location("/route");
}, [location]);
return (
// ...
);
}
this is not working because it is not supported in version 6 in react router dom
you must use
import {useNavigate} from "react-router-dom"
In v5, we could add trailing ? to route for optional parameters, but as in v6, the support for the same has been dropped, so what's the alternate way of writing the following piece of code?
<Route path="/cart/:id?" component={<CartPage />} />
react-router-dom#6.5.0+
Optional parameters have been re-introduced to the library. The docs haven't been updated from what I can tell, but v6.5.0 Release Notes include the details.
The above routes can be merged to a single route:
<Route path="/cart/:id?" element={<CartPage />} />
react-router-dom#6-6.4.5
After quite a bit of digging through the source code to understand how path parsing was different in RRDv6 from RRDv5, and turned up nothing really other than they no longer use path-to-regex, I hit up the repo's issues section and found this issue which outright states they don't plan to support optional path parameters in v6.
See FAQ: What Happened to Regexp Routes Paths?
It seems the suggested solution is to render 2 routes to match either path and render the same component.
Example:
<Route path="/cart/:id" element={<CartPage />} />
<Route path="/cart/" element={<CartPage />} />
or
<Route path="/cart">
<Route index element={<CartPage />} />
<Route path=":id" element={<CartPage />} />
</Route>
The latter is really only syntactic sugar around relative paths from "/cart" though.
As Drew Reese said, there is no support for the optional parameters in v6 (at least as of now).
I have ended up writing these little helper functions that register all nested routes for the optional parameters.
const registerOptionalParamRoute = (optionalParams: string[], element: Element) => {
if (optionalParams.length === 0)
return <Fragment/>;
const param = optionalParams[0];
optionalParams.splice(0, 1);
return <Route path={param} element={element}>
{registerOptionalParamRoute(optionalParams, element)}
</Route>;
};
const registerOptionalParams = (path: string, element: JSX.Element) => {
const params = path.split("/");
let basePath = "";
let optionalParams = [];
for (let i = 0; i < params.length; i++) {
if (params[i] === '')
continue;
if (!params[i].includes("?"))
basePath += "/" + params[i];
else
optionalParams.push(params[i].substr(0, params[i].length - 1));
}
return <Route path={basePath} key={basePath} element={element}>
{registerOptionalParamRoute(optionalParams, element)}
</Route>;
};
Then call it:
<Routes>
{registerOptionalParams('/component/:param1?/:param2?', <Component/>)}
</Routes>
For an example url /component/:param1?/:param2? and given component <Component/> it generates the following jsx element:
<Route path="component" element={<Component/>}>
<Route path=":param1" element={<Component/>}>
<Route path=":param2" element={<Component/>} />
</Route>
</Route>
I have also created feature request for optional parameters (https://github.com/remix-run/react-router/issues/8381), will see what feedback it will get.
Use wildcard:
<Route path="/cart/*" component={<CartPage />} />
It would be great if this could be described in the migration guide – or if there was at least a hint. So the recommended way of doing "optional" params really is ...
<Route path='/page/:friendlyName/:sort' element={<Page/>} />
<Route path='/page/:friendlyName/' element={<Page/>} />
Base react router doc
https://reactrouter.com/docs/en/v6/hooks/use-location
would not this work?
import * as React from 'react';
import { useLocation } from 'react-router-dom';
function App() {
let location = useLocation();
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
return (
// ...
);
}
import * as React from 'react';
import {useNavigate} from "react-router-dom"
function App() {
const location = useNavigate();
React.useEffect(() => {
ga('send', 'pageview');
location("/route");
}, [location]);
return (
// ...
);
}
this is not working because it is not supported in version 6 in react router dom
you must use
import {useNavigate} from "react-router-dom"
With the new React Router V6, the handling of routes changed a lot. I'm currently struggling to build the following structure
<BrowserRouter>
<Routes>
<Route path=":locale" element={<OtherRoutes/>} />
<Route path="*" element={<p>404</p>} />
</Routes>
</BrowserRouter>
The OtherRoutes component should return different paths based on the parent param, but this code does not work. I also tried with absolute paths, but this does not work as well.
const OtherRoutes = () => {
const {locale} = useParams();
if (locale === "de") {
return <Route path="seite" element={<GermanPage/>}/>
}
return <Route path="page" element={<EnglishPage/>}/>
}
Does anybody have an idea how to get this working?
Thanks
The solution was pretty simple. I just needed to make the first route :locale to match all routes like :locale/*
I don't really get how to constrain params with, for example a regex.
How to differentiate these two routes?
<Router>
<Route path="/:alpha_index" component={Child1} />
<Route path="/:numeric_index" component={Child2} />
</Router>
And prevent "/123" from firing the first route?
React-router v4 now allows you to use regexes to match params -- https://reacttraining.com/react-router/web/api/Route/path-string
const NumberRoute = () => <div>Number Route</div>;
const StringRoute = () => <div>String Route</div>;
<Router>
<Switch>
<Route exact path="/foo/:id(\\d+)" component={NumberRoute}/>
<Route exact path="/foo/:path(\\w+)" component={StringRoute}/>
</Switch>
</Router>
More info:
https://github.com/pillarjs/path-to-regexp/tree/v1.7.0#custom-match-parameters
I'm not sure if this is possible with React router at the moment. However there's a simple solution to your problem. Just do the int/alpha check in another component, like this:
<Router>
<Route path="/:index" component={Child0} />
</Router>
const Child0 = (props) => {
let n = props.params.index;
if (!isNumeric(n)) {
return <Child1 />;
} else {
return <Child2 />;
}
}
* Note that the code above does not run, it's just there to show what I mean.