React Router v4 Nested within Nested components - reactjs

Let's take this example
{/* NESTED ROUTES */}
<Route path={`${match.url}/:topicId`} component={Topic}/>
<Route exact path={match.url} render={() => (
<h3>Please select a topic.</h3>
)}/>
Nested routes work fine. However, how can I nest a Route within a nested route? For example:
path={`${match.url}/:topicId/edit`} component={EditTopic}
Is there a good/bad practice guide for how many levels you would nest? Any tips/guidance is appreciated!

how can I nest a Route within a nested route?
React Router v4 encourages you to think of Route just like any other component. It helps you compose your interface depending on what is happening in the url.
So - you shouldn't feel weird about nesting Routes or using them deep in your component tree to modify the interface in response to the url.
In your example, you could have further nested Routes either in the Topic component or in the function passed down as a render prop.
Is there a good/bad practice guide for how many levels you would nest?
This is a great question. Like so many other things, it comes down to taste. Be guided by making your code easy to understand for others and for yourself in six months time.
In some cases, it might be simplest to take the old fashioned approach and keep all your routes in one component. In other cases, it will be better to use four levels of nesting.
In my team, we have adopted v4 and use several layers of nesting. Along the way we had a lot of conversations to make sure everyone was clear on the approach.
Caution: If you use deep nesting in conjunction with e.g. redux connect or react PureComponent, be aware that these might block location updates to your nested Routes. You'll know this is happening when your Route doesn't respond to changes in the url. Use withRouter to solve this.

Instead of using match.url, you need to use match.path for building nested routes. According to match documentation
path - (string) The path pattern used to match. Useful for building nested <Route>s
url - (string) The matched portion of the URL. Useful for building nested <Link>s

Related

React Router - What is the difference between Route children and component props

I've got some troubles understanding what's the diferrence between this:
<Route path="user/:id" component={UserComponent} />
and this:
<Route path="user/:id" children={<UserComponent/>} />
inside Switch component when using React Router.
In both cases those components will render if the url will look like this "/user/4322". I'm reading React Router documentation but I can't understand this use case correctly (https://reactrouter.com/core/api/Route/route-props)
Okay, I got it:
https://forum.freecodecamp.org/t/react-router-what-is-the-difference-between-route-children-and-component-props/429503
#DanCouper from FreeCodeCamp forum explained it perfectly:
the first one is if you want to mount a new component when the route
matches. This is what things most often look like: you have a set of
routes and they all open a new “screen”, last component unmounts, new
component mounts, everything gets blown away. It’s the simplest way to
do things.
second one is if you want something to render all the time and not
remount. So for example, as in the example in the docs, you have a set
of navigation links, and you want to animate between them on route
change. If you used component this wouldn’t work, because the new
component would mount every time and the animations wouldn’t happen.
But because children is used, can still have the URL change in the
browser but not have everything be blown away every time the URL
changes.
children (and render) are going to be more fiddly to use most of the
time, because they cater to less common scenarios (in children's case,
preventing new component creation every time routes change, allowing
the UI to dynamically change based on route).

Passing props with React Router

So I'm still learning React, but I feel like I understand at least the "basics". However, Routing with React router and just general "architecture" is where I'm getting a little lost at.
I'm making a personal project app, but ill keep my descriptions/components generic for the explanation. Essentially I have 3 Main components right now:
<Table/> (Rendered in "App" and also the main / URL route, it's essentially just a bootstrap table. Also contains my state which is an array of objects (That will be eventually retrieved via JSON via a GET request to an API)
<Row/> (A single row of data on the table, represented by 1 object in the array of objects from the state provided in <Table/>. Also contains a button that takes you to the "View" page/component below)
<View/> (What is going to be a "View/Details" page for the Row)
So I imagine my route for <View/> is going to be something like <Route path="/:id/view" children={<View/>} /> (Keeping it super simple and generic right now just for the explanation)
Sidenote: How come sometimes I see <Route> wrapping a component and sometimes I see <Route> defined with the children prop pointing at the Component to render?)
Anyways...the button in <Row/> will be a <Link to={'{$props.id}/view'}> I think....however how exactly do I pass the props down to <View/> based on the :id param? I need to pass the data I receive in the <Row/> component onto the <View/> component (Because within the <Row/> component is where I link to <View/>.
I suppose ONE option would be to do something like let {id} = useParams(); and then do a GET request specific for that particular resource from the API. But considering I'm already passing the data down to each row and it's available in the state within <Table/> it seems like an unnecessary HTTP request.
Im probably making this more complex than I need to, but I need to pass the "data" all the way down from <Table/> to the <View/> component depending on the data id. And I'm not really sure how to set up React router to do that. Hopefully what I'm asking makes sense.
To answer your first question, I'll reference the react-router documentation as this is answered in the subsection named "route render methods".
The tldr of the above is simply that the "preferred" method of rendering components via <Route/> is by supplying actual children to the component. However, there are other methods and specific use cases that go along with each.
As for the core issue you're asking about: What you'll want to do is use an object in the <Link /> component that persists a state to the <View /> element that has all of the information that it will need. You can find react-router's documentation on this here.

Store state within the React Router component?

Ok, I'm pretty new to React in some ways and I am working on a simple react app that's 3 pages. I'm using react-router, partially so I can learn to use it.
On one page, I have a form that sets some state. The state obviously does not persist when you go to another route, because react-router renders a different component.
Solution #1
A solution is to have the main "App" component render one of the 3 pages/components (looking up the current route with params) and just store state there.
Solution #2
Another solution that seems obvious is storing the state in the Router component along with all the methods to update the state.
Then, I could just pass down the methods to update state to the components in the router. Seemed like a good idea, but I cannot seem to find anything online about anyone doing anything like that.
I see a lot of redux, but that's overkill for what I am doing.
So, is that possible? If it is, is there some reason that I cannot find anything about it? (Am I missing some terrible security thing or other gotchas that doing this would create.)
Instead of redux, i think you can also try react context. Ref
You can create a context in you App component and wrap your 3 page components in Context Provider to access the context. You can pass a map while creating a context, with values and setter,getter methods and use that map to change the values in your context map.
You can watch this talk for better understanding.
Hope this helps.
Redux is what you need. Check this below approach as well.
App component
class App extends Component {
state= {
test: 'test'
};
render() {
return (
<Route path="/" component={() => {<Home {...this.state.test}/>}/>
</div>
);
}
}

How to pass a property to a component via the reactjs router?

I have a reactjs router v2.8.1 in my app and trying to figure out how to pass properties to a component. This is what one of the routes looks like:
<Route path="/comp2" component={Component2} />
Suppose I am trying to go from Component1 to Component2 and want to pass properties from Component1 what would be a way to do this?
If you are using MemoryRouter or NativeRouter, you can send arbitrary state as explained here.
For web though, you should prefer taking dynamic data as either query params, or url parameters, as depicted in the links. The reason being that these are stored right within the address. This allows some more browser scenarios (e.g. bookmarks, in-private mode) to work as expected.

With react-router is ite possible to access context from a Route's onEnter prop?

I'm looking how to control which routes are available, based on a users privilege level which is in the context.
I found a nice example, but it relies on localStorage for storing user auth state. I would prefer to keep everything "in state".
I've been playing around for a while, but I can't seem to pass context into the onEnter() prop of a <Route ... />. My closest solutions are looking terribly hacky and I'm starting to think making react-router aware of context is a bad strategy.
Has anyone got an example? or can confirm the router is should not be making decisions based on context?
Thanks
Edit: I have been pointed to the react-router examples, it looks promising: https://github.com/reactjs/react-router/tree/master/examples
Although localStorage is pretty reliable, it might not be supported or user could delete it.
I would suggest you to use redux & react-redux instead, then you can easily access the state of your application, code would look something like this:
const Root = ({ store }) => (
function authenticateUser(nextState, replaceState, callback) {
const state = store.getState()
// do your thing - forbid, grant access etc...
}
<Provider store={store}>
<Router>
<Route path="/" component={App} onEnter={authenticateUser} />
</Router>
</Provider>
);
Moreover redux helps you scale react applications. It might be a little difficult and overwhelming to understand redux at first, but it's ok - because the effort is well worth it and will pay off.
Usage of redux with react-router.

Resources