react-router full path argument - reactjs

I am trying to do something with React Router and I can't seem to find a way to do it. I am using React Router 4.2.0.
I have some routes that looks like this:
<ConnectedRouter history={history}>
<div>
<Route exact path="/" component={Projects}/>
<Route path="/editor/:path" component={Editor}/>
</div>
</ConnectedRouter>
When I am on the page /editor, I would like to get the path argument with all the text that is put after even if it has '/' in it.
So if I am going to the page /editor/one/two path argument will be one/two.
If I am going to the page /editor/one/two/three path argument will be one/two/three.
Does anyone know if it is possible to do it ?
Thanks.

React router uses path-to-regexp for path matching.
Checking the path-to-regexp docs, you see that the library offers the option to match one-to-many parameters with a + prefix:
var re = pathToRegexp('/:foo+')
// keys = [{ name: 'foo', delimiter: '/', optional: false, repeat: true }]
re.exec('/bar/baz')
//=> ['/bar/baz', 'bar/baz']
In other words, /editor/:path+ matches /editor/one/two/three, and argument path would be one/two/three.

Related

React Router v6: get router path with params names

I have a route with dynamic path, using params:
<Route
path="/main/:id"
element={
<Page />
}
/>
When I reach that page, using React Router useLocation, I'm able to get the full path:
"/main/5432gt34"
Is there a way to retrieve the path with the params name instead of the actual path?
(output I'm looking for is:)
"/main/:id"
I'm hoping this might be possible since React Router is aware of the params inside the path and you can extract those with useParams
I don't think react router gives a way to do this but you could do something like this if you're trying to find an easy solution.
<Route
path="/main"
element={
<Page type="index"/>
}
/>
<Route
path="/main/:id"
element={
<Page type="id"/>
}
/>
then handle the type parameter in your page component.
I doubt that it's possible but you can always just pass it as property.
<Route
path="/main/:id"
element={
<Page
path="/main/:id"
/>
}
/>
Aside from that answer for this is here: react-router v6: get path pattern for current route
Yes you can do it by using useParams only if you know the prefix (You're sure that the path starts with "/main" and the rest is dynamic as you mentioned in your question):
let params = useParams();
useEffect(() => {
let dynamicPath = "/main";
Object.keys(params).forEach((key) => {
dynamicPath += `/:${key}`;
});
console.log(dynamicPath);
}, []);
Solution 1: You could pull the id from the string using a regex.
var location = useLocation();
var myRegexp = /main\/(.*)/;
var id = myRegexp.exec(location.pathname)[1];
Little explanation of the regex:
\/ allows us to do match with "main/"
(.*) gives the rest of the match as a second result in returned array.
Once you have the id you can store it a reactContext so that it can be used in any child component.
Solution 2: useRouteLoaderData
This solution would require you to refactor your router so that it one of the data routers but afterwards you should be able to do:
const user = useRouteLoaderData("id");

Routing query params without slash before it in react-location

Previously, I was using react-router-dom. With react-router-dom, I could define a route with query params like this:
<Route path="/p:productId" element={<Products />} /> // localhost:3000/p1 will match this route
But it seems like react-location doesn't work in the same way. The only way that I found is by defining the query params as a child of another route, but this leads to a slash before the actual letter 'p'.
The following example comes from the official documentation, but I'll rewrite it to match our own example.
const routes = [
{
path: 'p',
children: [
{
path: ':productId', // matches /p/:productId . localhost:3000/p/1 will match this route.
},
],
},
]
Is it possible to make it like the first example, which comes from react-router-dom?
Regardless of the answer, is it a bad practice to do it like the first example?

Generate Route dynamically - React

I am trying to do RBAC(Role-Based Access Control) using react js. I have seen some related question on Stackoverflow but they are not related whith my question. My question is:
There is a user and the user has a role and every role has its own list of menus assigned to it. Each menu has a path and component name.
What I need to do is that when a user sign in Route will be generated dynamically based on his role.
As we know the following is the normal way of defining routes in React. and it is static.
<Switch>
<Route path="/users" component={Users} exact />
<Route path="/items" component={Items} exact />
....
</Switch>
I need to generate every Route dynamically. This is what I have tried,
menus=[
{
name:"Users"
url:"/users"
component:"Users"
},
{
name:"Items"
url:"/items"
component:"Items"
}
]
<Switch>
{
menus.map(menu =>{
return(<Route path={menu.url} component={menu.component} exact/>)
})
}
</Switch>
But this is not working for me. When I try to navigate to /users it can't find the route. But when I use the first method it works correctly. Can you please tell me whats wrong with my code??
Here is a sample code that describes my problem
Here is a sandbox link
You are passing the component as a string to Route, you need to remove the quotes around them in your menu array.
menus=[
{
name:"Users"
url:"/users"
component:Users
},
{
name:"Items"
url:"/items"
component:Items
}
]
If your menu is dynamic from a DB and they are strings, then you will need to make sure the component is imported into the file, and then map it with an object like so:
import {Users} from './Users';
// assuming this is what menu will return as
const menus=[
{
name:"Users"
url:"/users"
component:"Users"
},
{
name:"Items"
url:"/items"
component:"Items"
}
]
const keyMap = {
Users: Users // mapping 'Users' string to <Users/> component
}
return (<Route path={menu.url} component={keyMap[menu.component]} exact/>)
Solution demonstration is available here

How Can I Have Optional *Non*-Parameter URL Parts?

This Stack Overflow question/answer explains how to define React Router (v4) routes which contain multiple optional parameter, eg.:
<Route path="/to/page/:pathParam1?/:pathParam2?" component={MyPage} />
However, it doesn't explain how to put optional text in-between those parameters, for instance:
<Route path="/to/page/:pathParam1?/otherParam/:pathParam2?" component={MyPage} />
// Should match /to/page/1 AND /to/page/1/otherParam/2
This was certainly possible in previous versions of React Router, but I can't see how to do it in the current version. Is there any way to specify optional parameter/non-parameter pairings, or even just optional non-parameters? Something like:
<Route path="/to/page/:pathParam1?/(otherParam/:pathParam2?)" component={MyPage} />
React router uses path-to-regexp - https://github.com/pillarjs/path-to-regexp
You can match optional non-parameters with a path like this:
const path = "/to/page/:pathParam1?/(otherParam)?/:pathParam2?"
And to test it:
const re = pathToRegexp(path)
console.log(re.exec("/to/page/1"))
// ["/to/page/1", "1", undefined, undefined]
console.log(re.exec("/to/page/1/otherParam/2"))
// ["/to/page/1/otherParam/2", "1", "otherParam", "2"]

React Router: How to render different Components with different parameters

Is it possible to have routes with different parameters and render different components based on the parameters?
For an example can I do:
<Route path="/SamePath/:param1" component={Component1} />
<Route path="/SamePath/:param2" component={Component2} />
If you are trying to have a the same route structure but render different components based on the value of the param, then you could try using render to decide which component to render based on the value of the param:
<Route path="/SamePath/:param1" render={ (props) => {
if (props.match.params.param1 == 'something') {
return <Component1 { ...props } />
} else {
return <Component2 { ...props } />
}
} />
If it is for lets say only two routes that will differ instead of using the capturing : just type in the name Eg.( samePath/organges samePath/apples) of the route if thats not the case and you want to capture a variarty range of to render differnt components you could use the regex routes feature of react router 4.. but that is much more complicated
In my project, I created a dictionary mapping possible values of the parameter to the desired component, and created <Route>s based on this dictionary.
<Switch>{
[{
['param1']: Component1,
['param2']: Component2,
}].forEach((component, param) =>
<Route
component={component}
exact
path={`/path/with/${param}`}
/>
)
}</Switch>
This will produces individual <Route>s for each path variation you wish to match. It will render different components for each route and, to me, seems in keeping with React-Router v4's dynamic routing ethos.
I encountered a problem: I couldn't access the value of the param via the match property passed to the rendered child. The Route matches against a specific path string (e.g. '/path/with/paramValue1) and not against a route descriptor with a parameter (e.g. '/path/with/:param'). This means thematch.params` dictionary passed to the child will not contain any keys or values. If you don't need the params in your child, this method will work fine. I wanted to inspect the param in the child so I had to look for a different method.

Resources