Programmatically set params in React Router v4 - reactjs

In React Router 4 I can define
<Route path="/calendar/:view/:year?/:month?/:day?" component={Calendar} />
to pass props from a URL to my Calendar component.
Inside the Calendar component, I want to programmatically set the URL params. The docs show how to do this using history.push(), but this requires you to format the URL yourself like history.push(``${view}/${year}/${month}/${day}``). But I want to update the URL without being coupled to the route structure, ideally something like history.push({ view: 'month', year: '2018' }).
Is this possible? It seems odd to me that React Router helps split up the URL into params, but doesn't provide a nice mechanism to set those params?

I do that by using match.path and generatePath. Here is an example:
this.props.history.push({
pathname: generatePath(this.props.match.path, {orderId: "latest", orderItemId: "latest"})
});
You can read about it here https://reacttraining.com/react-router/core/api/generatePath and here https://reacttraining.com/react-router/core/api/match.
Thats the cleanest solution I found so far for preventing the route component from knowing to much about where it will be allocated later in the path structure.
Maybe it fits your (or someone else's) need.

I would like to suggest that instead of history.push() as answered, you would use history.replace().
with replace you would not affect the history in case the user would want to navigate backward in the browser.
this.props.history.replace({
pathname: generatePath(this.props.match.path, {orderId: "latest", orderItemId: "latest"})
});

I managed to implement this using the path-to-regexp package that React Router uses:
this.props.history.push(pathToRegexp.compile(this.props.match.path)({ view: 'month', year: '2018' ));

I'm not sure if what you want is achievable, because if you pass the params to the react-router-dom, the params goes to the URL, but if you decouple the params with the URL structure, how will react-router know how to change the URL? The same URL may match multiple routes, with multiple params structures (and nested routes).

Related

React - How to navigate using a custom component instead of react-router-dom

I have a rooms list that I iterate, rendering the different rooms like this:
<Room room={room} key={room.id}/>
I want each room to redirect to their corresponding path (/rooms/:id). The only way of redirecting elements is via react-router-dom but I feel there must be a better way of achieving redirection in this case.
React router works fine, only thing you need is pass id to link
<Route path="/RoomsList/:roomId" element={<RoomCard/>}/>
in RoomCard you use hook
const {roomId} = useParams();
and change the component depending on the id.

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.

How do you pass data from one view to the other via react-router-dom without using url parameters?

I use react-router-dom v 4.3.1 for client-side routing. I'm pretty new to React and can't figure out how to pass data from one view to the other without using url parameters. In Angular, the Angular router has a data property where you can pass data associated with a route. An example would be:
const appRoutes: Routes = [
{
path: 'hero/:id',
component: HeroDetailComponent,
data: { title: 'Hero Detail' }
},
];
Can you do the same in react-router-dom? If not, how would you recommend I pass data in React?
Thanks in advance for the help!
<Route path="hero/:id" render={() => <HeroDetailComponent title= "Hero Detail" />} />
Read this: Pass props to a component rendered by React Router
Or if you are using <Link> you can use pass through location object
<Link to={{ pathname: 'hero/:id', state: { title: 'Hero Detail'} }}>My route</Link>
Well you Could use the context API to create a sort of global AppState that you could update in your first component and use in your second component.
You could also abuse the localStorage API by setting a key with the data in the first component and getting it in the other.
However both of these are workarounds that Shouldn't have to be used. Why do you want to redirect to a page but not pass data to it using URL parameters.
There'a several solutions. React being a library, not a framework, doesn’t force you into a single one.
One way is to use the context api. It’s like a link to an object shared between different components.
Another one is redux, which uses context underneath, and gives you a single store for the whole app. You changes values dispatching actions to the store, so it’s a bit tricky to learn the first time.
Using a stream library would open up a lot of different options, but it’s harder to get into. Check refract if you want to go this way.
A poor man’s stream approach that may serve you is using document as a bus to pass data arround, using addEventListeners to receive data and dispatch new customEvent to send it.
Next is the simplest one of all, share a simple object. Using imports form your components, you can import the same object on both and that will be a single instance where data can be shared. Simple JavaScript. It’s not the react way though, because changes won’t trigger a repaint on the component.

How to match similar routing conditions in a single path in react-router?

I have a component named Details and the route for that looks like the following:
const appRoutes=[
{ path: "/details/:id", component: Details }
]
Now if the user tries to access only the "/details" route, I want to redirect them to an external URL, let's say "https://www.google.com". Currently I'm doing it by adding the following right after the line above in appRoutes.
`{ path: "/details", component: () => window.location.assign("https://www.google.com") }`
Is there a better way to do it in one line instead of two, so that I don't have to specify the case for /details and details/:id separately like I'm doing here? The expected behavior here is that if the route is details/:id then render the Details component and if it is just details then send them to an external URL.
By the definition of your idea, these are separate routes. So, it's for the best to use separate route statements to define them. If anything, this just makes your code more declarative and clear to other readers.
What I would recommend is to to pass exact: true to the "/details" route and place that before your "/details/:id" route. React Router has an easier time deciding which route to show when your exact routes are defined before your parameter-based routes.
Hope this helps!

Pass data to react routes

When transitioning from one react route to another, I can pass strings via URL parameters
this.props.history.replace({
pathname: `${this.props.match.url}/newRoute`,
search: '?someData=someValue'
})
But what if I want to pass something more complex, e.g. a deeply nested JSON object? Is there a way such data can be passed from one route to another without using Redux?
The project in question is using react 14.4.2 and react-router 4.3.1.
Passing complex data between routes is usually a bad idea. For example, what happens when the user refreshes the page on the route? When the page reloads, all the app state and whatever you passed through to the route will have been lost.
As a rule of thumb, each route should be capable of gathering or refreshing all of the data it needs from scratch using only the information in the url (paths, path params, or query strings).
If that doesn't quite fit with your use case, consider the possibility that this part of your app doesn't need its own bookmarkable route/url, and you're just implementing a transitory UI change which is ok if it gets lost upon page refresh.
Example for passing parameter in Route:
<Route path="/:id" component={Child} />
const Child = ({ match }) => (
<div>
<h3>ID: {match.params.id}</h3>
</div>
);
please refer : https://reacttraining.com/react-router/web/example/url-params
I think possible ways can be:
1- By using location state object, we can pass the data into that and it will be available in new page. Issue with this approach will be, after refreshing the new page, that data will not be available.
Like this:
this.props.history.replace({
pathname: `${this.props.match.url}/newRoute`,
search: '?someData=someValue',
state: {
....
}
})
And access it in new page by: this.props.location.state
2- Using localStorage, set the data into localStorage before navigating to new page and then read the same data in new page.
Like this:
navigate() {
localStorage.setItem('data', obj);
this.props.history.replace({
pathname: `${this.props.match.url}/newRoute`,
search: '?someData=someValue',
})
}
I will suggest to use the 2nd approach, its not a good idea of passing big object in route.

Resources