I am using path variables in my Route components. Initially, I had
<Route path={"/:brand/"} component={HomeContainer} />
but I had to change path attribute to
path={"/:brand(brandOne|brandTwo)/"}
because incorrect URLs appeared to be valid. The code above works for my 2 brand sections.
How would you suggest I do this for my products container as I have many products and I don't want random incorrect URLs to show an empty page with SiteHeader and SiteFooter.
Thanks
If you have many params that you want to validate for, you can simply store the valid ones in an array and check for the param while rendering, If the param is not among those, redirect to either 404 or home page
render() {
const { match} = this.props;
if(!validParams.includes(match.params.brand)) {
return <Redirect to="/404" />
}
// return normal component here
}
Related
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
According to the accepted answer to this question, React Router 4 doesn't match query parameters anymore. If I go from a URL matched by one of my <Route>s to the same URL with a different query string, the content doesn't seem to change. I believe this is because navigating between URLs that match the same <Route> doesn't change the content, but please correct me if I'm wrong. Given this, how do I use React Router for a set of URL's that need to differ only by query parameter?
For example, many search engines and other sites that use search bars, including the site I am working on, use a query parameter, commonly q or query. The user may search for one thing, then decide that is not what he/she wants and search for another thing. The user may type in the second URL or search with the search bar again. There isn't really a place for the search term in the URL path, so it kind of needs to go in the query string. How do we handle this situation?
Is there a way, with React Router, to link to a URL that only differs in the query string and change the content, without refreshing the entire page? Preferably, this wouldn't require any external library besides React and React Router.
Try the render function prop instead of component prop of Route. Something like this:
<Route render={props => {
// look for some param in the query string...
const useComponentA = queryStringContains('A');
if(useComponentA) {
return <ComponentA {...props}/>;
} else {
return <ComponentB {...props}/>;
}
}}/>
There are 2 ways to do that:
1) Use location.search in react component to get the query string, then pass it to child component to prevent re-rendering the whole component. React-router has the official example about this.
2) Define a regex path of router to catch the query string, then pass it to react component. Take pagination as an example:
routes.js, for router config you can refer this
const routerConfig = [
{
path: '/foo',
component: 'Foo',
},
{
path: '/student/listing:pageNumber(\\?page=.*)?',
component: 'Student'
},
Student.js
render() {
// get the page number from react router's match params
let currentPageNumber = 1;
// Defensive checking, if the query param is missing, use default number.
if (this.props.match.params.pageNumber) {
// the match param will return the whole query string,
// so we can get the number from the string before using it.
currentPageNumber = this.props.match.params.pageNumber.split('?page=').pop();
}
return <div>
student listing content ...
<Pagination pageNumber = {currentPageNumber}>
</div>
}
Pagination.js
render() {
return <div> current page number is {this.props.pageNumber} </div>
}
The 2nd solution is longer but more flexible. One of the use cases is server sider rendering:
Apart from the react components, the rest of the application (e.g. preloaded saga) need to know the url including query string to make API call.
I have to access value to optional param(s) form a react router (React Router v4). Now problem is as we have avoid param(s) sequence.
For example as per following react router code we have to access pathPraram2 without define pathPram1 :
<Route path="/to/page/:pathParam1?/:pathParam2?" component={MyPage} />
You can use withRouter hoc from the react-router-dom (or from native router).
This will pass some routing props to your component.
Validate it with flow if you want to:
type PropType = {
match: {
params: {
pathParam1?: string, // '?' to emphasize its optional
pathParam2?: string,
},
},
};
In your component you can use them by simply checking for their existence.
if (this.props.params && this.props.params.pathParam1) {
// do something
}
I hope this helps.
withRouter: https://reacttraining.com/react-router/native/api/withRouter
Also, extending on #Shubham's answer, I recommend having these optional params with identifiers.
Example:
http://example.com/page/identifier-1/param-1/identifier-2/param-2
http://example.com/page/identifier-2/param-2
http://example.com/page
This way you can define a route based on page, and in the component you can check based on identifier the param your route contains.
For a route with params like such:
{
articleId: 'abc',
commentId: 'xyz'
}
To create a route mentioned above:
<Route path="/page/:key1?/:value1?/:key2?/:value2?" component={MyPage} />
Now to access these in component:
if (!this.props.match || !this.props.match.params) {
return;
}
let articleId = null;
let commentId = null;
if (
this.props.match.params.key1 &&
this.props.match.params.value1
) {
if (this.props.match.params.key1 === 'articleId') {
articleId = this.props.match.params.value1;
}
if (this.props.match.params.key1 === 'commentId') {
commentId = this.props.match.params.value1;
}
}
if (
this.props.match.params.key2 &&
this.props.match.params.value2
) {
if (this.props.match.params.key2 === 'articleId') {
articleId = this.props.match.params.value2;
}
if (this.props.match.params.key2 === 'commentId') {
commentId = this.props.match.params.value2;
}
}
then just simple check if articleId or commentId is null or not.
I think this will lead to a better url formation, though it increases conditions inside the component to get the parameters.
However in Shubham's answer, you will have less conditions and a different url formation. It's upon you to decide which one is preferable for you to use as per your use case.
Hope it helps.
It isn't directly possible for the Router to have an optional path param directly before the other
Consider the case
<Route path="/to/page/:pathParam1?/:pathParam2?" component={MyPage} />
Now say you wish to have pathParam1 and not pathParam2, so your route path will be
/to/page/pathpage1
Now consider another case when the pathParam1 is not passed but pathParam2 is, so route path looks like
/to/page//pathpage2
Now here pathParam1 will match as empty whereas the pathParam2 will match to pathpage2 which will work and you can access it like this.props.match.pathparam2
however it not clean in terms of the url as it contains two //. As better way would be to introduce a fixed path in between the params
<Route path="/to/page(/:pathParam1?)/somepath(/:pathParam2)?" component={MyPage} />
Lets say we have two components for Post and Project. Each post and project has slug. I want to access post and projects by slug. So we have routes:
<Route path="/:post_slug" component={PostComponent} />
<Route path="/:project_slug" component={ProjectComponent} />
And for example when you visit some post pr project, an action gets called to fetch its data by params. Something like this:
this.props.fetchPost(this.props.match.params.post_slug)
this.props.fetchProject(this.props.match.params.project_slug)
If I do only one component accessible by slug only, then it works. But if I want to access two different components by slug, then it's messed up. And i.e. when I visit post, react thinks that I'm visiting project.
Maybe there is some way to handle such case in react-router-dom?
There is no way to do this, since the router can't differentiate between the two routes. For example, if I were to navigate to site.com/my_slug, the router wouldn't know whether my_slug is supposed to be a :post_slug or a :project_slug. It's sending you to a project when they're both present because it interprets them to be the same route and so it uses the last one you define.
An alternative you could try is being a bit more explicit with your routes, like so:
<Route path="/post/:post_slug" component={PostComponent} />
<Route path="/project/:project_slug" component={ProjectComponent} />
You could do this if you put the detection logic in another component, like this:
<Route path="/:any_slug" component={AnyComponent} />
Then let that component figure out what type of ID you have and render the desired sub-component accordingly:
const AnyComponent = props => {
if (slugIsProject(props.match.params.any_slug)) {
return <ProjectComponent id={props.match.params.any_slug} />;
} else {
return <PostComponent id={props.match.params.any_slug} />;
}
}
This way the router always renders the same component (AnyComponent) but that component appears differently depending on what the ID is determined to be.
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.