React-router matches wrong component with similar match - reactjs

So I am developing an app in react and have react router lib to show different possible URLs in my app. I have the following routes, defining which component will be rendered, depending on the URL:
<Route path={`/products/:address/:page`} component={Products} />
<Route path={`/products/:productId`} component={ProductDetails} />
<Route render={() => <NotFound/>}/>
The 1st one expects something like this: /products/store/1 to match the products and show the products page.
The 2nd one expects something like this: /products/5e996d123e1e0816242aed80 and shows the product details.
The 3rd one is a fallback UI, if nothing matches
My problem is, that if I am in the 1st URL and delete the page (1), eg /products/store/, react router gets confused and tries to match the 2nd route.
In both components, I load the params incomponentDidMount like this:
this.props.match.params.address, this.props.match.params.page for the 1st component and this.props.match.params.address for the 2nd.
Ideally, I would like to render the 3rd route, if the 2nd argument of the 1st route is not present.
One possible solution would be to change the 2nd Route from /products/:productId to /product/:productId, so it won't get confused.
Is there any better solution for this?

Related

React router with multiple params

I have a page route /movies?categoryId=21213
on this page, I have a section of actors, on the click it's should redirect to /movies?categoryId=21213/actor?actorId=234324234
how should I describe correctly to render my latest component with an actor?
I tried
<Route path=`/movies/:categoryId?/:actorId?`/>
but that's not working
There are two ways to approach this. Either go with query params or path params.
Path params
You route:
<Route path='/movies/:categoryId/:actorId' />
Here you will have route that has two path params /movies/1/2.
Query params
Your route
<Route path='/movies' />
So your route is only /movies but you pass query params to it and handle them in your component.
Example of a route with query params /movies?categoryId=1&actorId=2.
You can use useHistory hook for that purpose in your routed component.
Personally i preferred to use query params because they are easier to handle in your component, but you can pick your way from these two examples.
In your question, code is a bit wrong, because path params dont need ? to be present in a route.

Show/Hide route components in react

I am working on a React application which has routes like so:
<Switch>
<Route path="/edituser/:username" component={EditUser}/>
<Route path="/createuser/:type" component={EditUser}/>
<Route path="/listusers" component={ListUsers}/>
</Switch>
ListUsers component shows a table with pagination where each component in the table has a link which points to /edituser/:username.
I can edit users by clicking on the item in the table but as expected with react, once I go back to listusers/ the component is loaded again and I will be on the first page of users. I want to be on the page from where I accessed the user in the first place.
What is the best pattern to achieve this? I thought about passing in the page number to /edituser and then back to /listuser but then again I have to load all the paginated results again. Is local storage the only option? Any pointers are much appreciated.
There are two solutions for your problem:
1) Pass last active page ad route parameter and set your pagination accordingly.
<Switch>
<Route path="/edituser/:username" component={EditUser}/>
<Route path="/createuser/:type" component={EditUser}/>
<Route path="/listusers/:pageNumber" component={ListUsers}/>
</Switch>
on your componentDidMount you can use it to set state. For example :
const pageNumber = this.props.match.params.pageNumber;
2) Pass state prop in your routing. For example :
<Link
to={{
pathname: '/listusers',
state: { pageNumber: 1 }
}}/>
on your componentDidMount you can use it to set state. For example :
const pageNumber = this.props.location.state.pageNumber;
You could add the page number to the URL in the /listusers endpoint. Maybe something like /listusers/2 or /listusers?page=2 this way, when you hit the browser's back button, you're directly there. One last thing you could do but I wouldn't advise in this case is to store the page number in the history state.
As a rule of thumb, in order to get back, prefer using the history than using local storage.
If you really want to keep the data in memory, you can always use a store that is in a higher component (the root component for example) and keep the previous query over there. However you'll need to be careful about a lot of routing issues in such cases:
cache invalidation: the data changed on the server side in the mean time
user somehow gets back to a page with another table page number
loaded user presses the browser back button

how to have the same route path hit different components

I'm having an issue with react router and its routing path.
I have a couple of links, say
localhost:3000/a
localhost:3000/b
localhost:3000/c
and my route is set up as so:
<Route exact path="/:cat" component={Post} />
My issue is that whenever I go to one of the three URLs, i.e. 1 -> 3, it will only load page 1, as all of them meet the criteria (i.e. path="/:cat"). Am I correct to assume that it won't render each path as they are referred to as ONE route, hence it doesn't need to be rendered as the "state" hasn't changed?
Its one route and any matching path (/a or /b) will render that Post component.
<Route exact path="/:cat" component={Post} />
This route will allow you to render Post component for each url, that starts from / and this component will have the actual url inside this.props.routeParams.cat.
You can use this prop in your Post component to call the appropriate child component. i.e Check if this.props.routeParams.cat = a , then call <ComponentA />.

ReactJS Values In The URL Always Visible

I have a filter system for my products in ReactJS which basically has the following:
http://localhost:3000/category/women/subcategory/tops/sorting/null/price/null/size/null/color/null/brands/null/merchants/null
The Route is as follows:
<Router>
<Route path="/category/:cat/subcategory/:subCat/sorting/:sorting/price/:price/size/:size/color/:color/brands/:brands/merchants/:merchants" component={Products} />
</Router>
The Problem is that I want to show filters in the URL in when they have a value other than null. Current my component works but I have to display every single filter in the URL with a null value by default, this is causing my URL to be extremely long. The only way I thought possible was to do a permutation combination of all the possible URLs in the filter and direct them all to { Products } which is extremely silly. There must be something in the Router component that I'm missing?
You need to use optional params in this case.
As and example if you want to accept both sorting/ascending/price and sorting/price you can write your path as follows assuming you use react router v4.
<Router>
<Route path="sorting/:sort?/price" component={Products} />
</Router>
You can read more about this here: React Router with optional path parameter

Using multiple params with React Router?

<Route path="/:user" component={Home}>
<Route path="/:thing(/:version)" component={Thing}/>
</Route>
So, I've got two dynamic objects in my application that I'd like to be controlled by route params in react-router. Using the code above, both /0 and /0/3 take me to Home. I need /0/3 to take me to Thing. I'm not sure what I'm doing wrong here... Does react-router even support multiple dynamic params next to each other like this? I couldn't find anything in the docs.
What happens here is that you've given React Router two paths that can both match on /anything. By default then React Router matches the first one it can find.
To dig deeper, if I go to /pudding, React Router can't know if you meant /:user or /:thing. Since /:user occurs first, that option will be chosen.
You also need to make sure if nesting routes is what you want. Currently, your Thing route is nested below Home, which means that it is rendered via this.props.children in your Home component. So, for your current Thing route, Home will always be rendered too, with Thing as a child. If your Home component doesn't render this.props.children, Thing will not be shown.
I suspect you just want two different pages. What you could do to achieve that is the following:
<Router history={history}>
<Route path="/user/:user" component={Home} />
<Route path="/:thing(/:version)" component={Thing}/>
</Router>
This will make every /user/name go to the Home component, and every other /random (with an optional extra level) will go to Thing. If you wonder why in this case React Router doesn't take /user/name to the Thing route, it's because it still matches in the order your routes are specified. Because your Home route matches the requested URL, no siblings of this route are tested anymore.

Resources