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/*
Related
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"
I would like to set the routes for the languages my webpage accepts, something like
import Component from '../src/component/component';
<Switch>
<Route exact path="/es" component={(props) => <Component language="es" />}/>
<Route exact path="/en" component={ (props) => <Component language="en" />}/>
</Switch>
But the languages that are accepted depend on a configuration file, and more can be added in the future. So I would like to be able to add these routes depending on the values of this file.
The option I've seen is to add the routes like this:
}/>
But don't want to accept any possibility, just some the ones I need.
Is there a way to create routes to accept several possible routes but not any route?
thanks.
You can write a simple mapping for the accepted languages and then loop over it to render the Routes
const languages = ['es', 'en'];
import Component from '../src/component/component';
...
<Switch>
{languages.map(lang => {
return <Route
key={lang}
exact
path={`/${lang}`}
component={(props) => <Component {...props} language={lang} />}/>
})}
</Switch>
The Switch-component in react router will render the first component that does not return null, so we can hijack this behavior to accomplish what you need.
First, we define an object mapping languages to components:
const langs = {
en: ComponentEn,
es: ComponentEs,
};
Then we can define a language selector component:
const LanguageSelector=({match})=>{
const language = match.params.language
const Component = langs[language];
if(Component) return <Component/>
else return null;
}
Now we can use it in our routing
<Switch>
<Route path="/:language" component={LanguageSelector}/>
<DefaultComponent/>
</Switch>
Of course you can replace the DefaultComponent with whatever you want
I'm trying to implement React Router with query params like so http://localhost:3000/login?Id=1, I was able to achieve it only for my login route that too if I put path as http://localhost:3000/ which then redirects , however, I want to implement across the application. It matches nomatch route if I implement on other routes. This is how my index.js looks like, Can someone guide me how can i go about implementing all routes path including query params ?.
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route
exact
path={`/`}
render={() => {
if (!store.getState().login.isAvailable) {
return <Redirect to={`/login?Id=${Id}`} />
} else {
return <Dashboard />
}
}}
/>
<Route exact path={`/login`} component={Login} />
<Route exact path={`/signup`} component={SignUp} />
{Routes.map((prop, key) => (
<Route path={prop.path} key={key} component={prop.component} />
))}
<Route component={NoMatch} />
</Switch>
</BrowserRouter>,
document.getElementById('root')
)
There are two ways about to accomplish what you want.
The most basic way would be on each "page" or root component of each route, handle the parsing of query params.
Any component that is the component of a Route component, will have the prop location passed to it. The query params are located in location.search and that will need to be parsed. If you are only worried about modern browsers, you can use URLSearchParams, or you can use a library like query-string, or of course, you can parse them yourself.
You can read more in the react-router docs.
The second way of doing this, really isn't that different, but you can have a HOC that wraps around each of your "pages" that handles the parsing of the query params, and passes them as a list or something to the "page" component in question.
Here's an example of the basic way using URLSearchParams:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// this is your "page" component, we are using the location prop
function ParamsPage({ location }) {
// you can use whatever you want to parse the params
let params = new URLSearchParams(location.search);
return (
<div>
<div>{params.get("name")}</div>
// this link goes to this same page, but passes a query param
Link that has params
</div>
);
}
// this would be equivalent to your index.js page
function ParamsExample() {
return (
<Router>
<Route component={ParamsPage} />
</Router>
);
}
export default ParamsExample;
EDIT: and to clarify, you don't need to do anything on your index.js page to make this work, the simple Routes you have should work fine.
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.