how to use react-router for dynamic url - reactjs

I am building extremely stretchable react app and I am using SSR fro SEO.
but I don't know how to handle URL
Like: user can directly land on this URL, domian.com/delhi/cardiologist
In the backend, I have a system of identifying the components for URL, which is for this case is "Speciality" component in "Location: component
I can't go like this
<Rout path={"delhi"}>
<Location/>
</Route>
cause for this case it's Delhi. but for another user, it will be a different city name, I want to reflect the different city in URL but to render same component due best SEO.
SEO is heart of my app.
I am literally new to this kind of things.
please guide me.

I would recommend adding some structure to the path that makes it easier for you to find the location, say:
<Route path='/search/:city/:specialty' component={Specialty} />
So this would match domian.com/search/delhi/cardiologist. You can customize it to whatever makes sense, it could even be a short letter like just /go/ if you are concerned about URL length because of SEO.
Then, inside the Specialty component, you can use match.params.city and match.params.specialty to get the parameters that you are looking for:
function Specialty({ match }) {
const { city, specialty } = match.params;
return (
<p>Search for {specialty} in the city of {city}</p>
);
}
You could still implement a catch-all route using the path that you want:
<Route path='/:city/:specialty' component={Specialty} />
However, that would require always setting the route at the end, so that it only lands on your <Specialty /> component if it doesn't match any other path that you have.

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.

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

React Router on pages with URLs that are not known in advance

I have a massive preexisting website into which some new pages are going to be added that are being built on (client-side) ReactJS. The routes at which the new ReactJS pages will live are deep in the site: The ReactJS pages have to play nice with the existing URL structure, and that means that in a lot of cases, they won't know what their base URL is until they're deployed to production. (In some cases, they still won't know, because the upper parts of the URLs, the parts before the ReactJS page, are parameterized and will be changing on the fly.)
However, React Router seems to like absolute paths, and doesn't seem to play well with partial/relative/unknown paths. I've solved this by querying document.location.pathname upfront when everything loads — but it feels clunky, like React Router should be able to handle these kinds of relative paths out-of-the-box.
So here's my code, which does work:
const baseRoute = document.location.pathname;
...
render() {
return <Router>
<Route path={baseRoute} component={App}>
<IndexRoute component={HomePage}/>
<Route path={baseRoute + "/sub-page1"} component={SubPage1}/>
<Route path={baseRoute + "/sub-page2"} component={SubPage2}/>
<Route path={baseRoute + "/*"} component={NotFoundPage}/>
</Route>
</Router>;
}
...
render() {
return <div>
<IndexLink to={baseRoute}>Home</IndexLink>
| <Link to={baseRoute + "/sub-page1"}>Sub-Page 1</IndexLink>
| <Link to={baseRoute + "/sub-page2"}>Sub-Page 2</IndexLink>
</div>;
}
But this seems like a very clunky, brute-force way to handle it. It means that anything that needs a path needs to have access to that baseRoute variable, which is computed once at startup. It means that everything that uses routing needs to remember to use the baseRoute variable as well. And, to make matters worse, the code can't really be as simple as what I have above, since the incoming URL could contain a child route that only React Router knows about, so in some cases, string massaging is needed to properly compute the baseRoute.
It looks like they may have solved this issue more generally in React Router 4 by adding a basename property, but we're on React Router 3 right now.
So is there a better solution in React Router 3 to the app being hosted at an unknown URL, or are we stuck with passing around a derived baseRoute property?

React/Redux Where to save State

I am having trouble understanding some ReactJS and Redux principles,
In Redux we have a store and a set of reduceres that give your final state ( this part I get).
A Component receives props from their parent component, in case of Redux it passes the full state down with the Provider Component.
Now, lets assume this use case, I have my app state something like this :
auth : { name,id, ..etc} -> this is actually a JWT Token with a set of claims...
I am building a Container (Page) to be able to edit user profiles, the catch here , and where I am having trouble, I also want this page to handle a arbitrary user edit (in case admin is editing a account).
I am rendering my Page by using redux-router like this :
<Route path="/user" component={RequiresAuth(UsersPage) } >
<IndexRoute component={RequiresAuth(UsersOverview) }/>
<Route path="/user/overview" component={UsersOverview} />
<Route path="/user/account" component={AccountManagement} >
<IndexRoute component={RequiresAuth(AccountManagement) }/>
<Route path="/user/account/:userid" component={AccountManagement} />
</Route>
So reading the docs, I assume my props will have a userid which will override my default user if present.
Questions:
Is this approach correct? The Route Handling?
If I pass a userid param on the route, how do I proceed to load the user info and where? The way I see it, I need to dispatch an action, but I am not sure where to set the state , if on the store, or the component.
Also, Where would I load my user information? (constructor comes to mind)... or should it be in WillMount/WillReceiveProps?
Thank you.
If you really have a hard time to understand React/Redux principles i suggest you to try this tutorial :
https://github.com/happypoulp/redux-tutorial
It helped me a lot when i first started with React/Redux. By the way it's a bit hard to really answer your question because you re asking specific questions on a really specific case :)

React-router: How can props be passed down to child components?

I'm not sure if I'm approaching this in the right way at all, so a bit of background first:
Let's say I have countries and cities in each country that I'm going to fetch from two stores, CountryStore and CityStore in the topmost route (view1). I'm trying to set up react-router so that I have nested routes like this:
<Route name="app" path="/" handler={App}>
<DefaultRoute name="view2" handler={View2}/>
<Route name="view1" path='country/:countryId' handler={View1}>
<DefaultRoute name="view3" handler={View3}/>
<Route name="view4" path='city/:cityId' handler={View4}/>
< More nested routes />
</Route>
</Route>
So from what I've been reading up on, I've understood that I should have state in my top view1 and pass everything down as props to view3, view4 and any others.
So here's a fiddle where I've got things working by passing down props with the route handler in view1, and from links I'm passing params based on which each view can filter out what they need. But I'm wondering if there's a smarter way of doing this by passing down the props I'm going to need with each link somehow. Meaning that if in my city list I'm already iterating through all the city objects, can I with the city link pass down the current city as props somehow, so the city view wouldn't need to filter out its city each time?
I hope the question is clear enough, I'm thankful for any advice!
Your question is a little confusing (maybe because of the very generic variable/view names), but I think I see what you're getting it.
I think there are two parts of the answer: can you do this, and should you do this.
I believe it's definitely possible that you can do what you're suggesting. If you look at the params in View1, you can see that when the url is country/1 the params are { countryId: "1" }, but when the url is country/1/city/2, the params are { countryId: "1", cityId: "2" }. So, based on this, View1 could see the cityId param and decide to pass an additional property to its child, for example:
// In View1
var props = {
country: testCountry
}
if (this.props.params.cityId) {
props.city = (get the city object here);
}
return (
<div>View 1 wrapper: countryId {countryId}
<RouteHandler {...this.props} {...props}/>
</div>
);
However, I'm not really sure that you should. This is breaking the encapsulation provided by React Router; you're coupling two views by giving View1 direct knowledge about how one of its children, View4, works.
If you're really concerned about the performance hit of iterating over the cities more than once, it may be worthwhile to provide an ID-indexable data structure that View4 can use to look up the city directly (e.g. var city = this.props.whatever[this.props.params.cityId]).
Taking a step back, I personally thing the "get all the state and pass it down as props" that Bill describes in the answer you linked to might break down a bit in applications that rely on routing. I consider each view passed to the router via <Route handler={...} /> to be a view-controller—a miniature standalone app, as it were. Doing so, as Bill mentions, gives a better encapsulation of the domain that the route/route handler is dealing with, and I don't think that it makes debugging more difficult, as the routes declare exactly which view is acting as the top-level view-controller for any given URL (so if some data looks wrong you just go immediately to the view-controller for the current route).
If you use a nice composition strategy to connect your route handlers to flux stores (see the code at this Fluxxor issue as an example), it makes it even clearer how the data gets from the stores into the components.

Resources