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

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.

Related

Update react Context from children or alter component structure?

This question focuses on the Header component: So in my app. I have a something that looks a bit like this with a few more components and plenty more routes:
return (
<SiteLayoutContext.Provider value={siteConfiguration} >
<div className="topContainer">
<BrowserRouter>
<div className="header">
<Header/>
</div>
<div className="mainWrapperContainer">
<div className="contentWrapper">
<Route exact path='/login' component={Login}/>
<Route exact path='/home' component={Home}/>
<Route exact path='/about' component={About}/>
<Route exact path='/collection/:collectionName' component={CollectionLandingPage}/>
<Route exact path='collection/:collectionAlias/id/:itemId' component={ItemView}/>
</div>
<Footer/>
</div>
</BrowserRouter>
</div>
</SiteLayoutContext.Provider>
)
What I'd like to accomplish:
I can currently set a logo for the header. This is great, but I'd like the user to be able to select a custom logo, one that will replace the global default, if they are within a collection.
To define 'within' a collection, the route will start with /collection/ so in the example below, both the ItemView and the CollectionLandingPage should have this custom logo.
I need to be able to tell my header that it's currently within a collection, and I want to limit renders and redraws to improve performance. I have an api endpoint that I fetch to grab collection info, which would tell me if a custom logo has been set, and what the href should be.
What I've tried: My original thought was to rip the header out of the main return() shown above, and instead place it within every component, that way I can use React's useLocation() hook to check the the url, and only fetch the collection's logo if1) we're within a collection and 2) a custom logo has been set.
My issue with this method is that I now have to enter the Header within every single component, instead of how it is currently implemented.
My second thought is to wrap it all within some kind of context, consume it in every component, and update it based on the url, which would then fetch the relevant info, but this just feels off. I suppose I could fetch the information for the collection and update a new CollectionInfoContext every time I hit /collection/:collectionName which would only update when the collectionName changes?
I'm kind of just looking for suggestions on how to implement this because well... I feel like there's an option I haven't listed that's probably much easier and more reasonable... as is the stackoverflow standard.
Disclaimer: I'm a react context api novice
If you're not using Redux or another state management solution, a context is definitely a reasonable way to share data across your application, UI themes are actually one of the recommended use cases in the React docs.
The only issue I could see is that changing the state for only certain components would require a way of resetting the context for other places where you just want a standard logo. But reading the URL name could solve that by simply storing the path name and using it inside of Header to render the custom logo if the path includes collections. If Header can read the path name all on its own, you may not even need a context if that's the only condition that's dynamic everywhere.

how to use react-router for dynamic url

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.

Redirect react-router-redux push within component lifecycle

So I have a localised site, and need to redirect routes in certain cases. For example, if a user had their UI set to Spanish, and went to mysite.com/about, they would need to be redirected to mysite.com/es/about.
So basically, the routes are duplicated via <Route> components from react-router:
{Object.values(UILanguages).map(locale => {
return (
<Route path={`${locale.basepath}`} key={locale.basepath} locale={locale} component={App} status={200}>
...routes go here
</Route>
)
})}
It would be simple enough to check within the componentWillMount lifecycle method of <App> to find out if I'm on the wrong <Route> component, but then I would have to prepend every single link in my application, which I don't want to do. Instead, I would like dynamic redirecting for foreign languages, as displayed in the first paragraph.
How is this possible?
Use the react router hooks instead it will be cleaner (smaller components) and simpler than using the component lifecycle.
https://github.com/ReactTraining/react-router/blob/master/docs/API.md#onenternextstate-replace-callback
in your case you need to use all 3 arguments because the first one is the next state the second is the redirection you want to make and the third is a callback to call when you are done testing values.
i can give you an exemple about how to make this but i don't really know how your app is structured so instead check this link , it helped me a lot:
https://www.youtube.com/watch?v=JicUNpwLzLY&t=359s
hope this helps!
ps: in some cases you need to define the onChange hook too example : pagination...

How to access value of a routing parameter in react?

I want to have a 'dynamic route' for a section of my app, which was done like this:
<Route path="path(/:id)" component={Component} />
So far this works, but in Component I would like to access the value of id because it changes a bunch of things depending on which it is. How can I do that?
In your component, you would access this via props.
this.props.params.id
Here is the guide from react-router that goes into more detail as well.
https://github.com/reactjs/react-router-tutorial/tree/master/lessons/06-params

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 :)

Resources