In the react (15.4.1) docs, it says that instead of using props.children for including components, it is also possible to use custom properties props.left and props.right to include multiple children in components with "holes".
How can this approach be realized when using React Router (3.0.0)? The component={MyComponent} syntax does not seem to allow including multiple children.
I want to able to write something like this:
<Router>
<Route component={App} >
<Route component={Page}>
<Route page='/' component={Header}> <-- 1st child of Page
<Route component={Logo} /> <-- 1st child of Header
<Route component={Nav} /> <-- 2nd child of Header
</Route>
<Route page='/' component={Home} /> <-- 2nd child of Page
<Route page='/custom' component={Custom} /> <-- 2nd child of Page, 2nd route
</Route>
</Route>
<Router>
I am aware that I can do the nesting within the components, but then I have various components imports spread accross my files, which increases dependencies and makes it hard to track which component is used where.
I would like to keep all component dependencies in the one file that defines the routing structure.
Related
I've had two routes:
<Route path="/client/:id/members" component={Members} />
<Route path="/client/:id/members/:mid" component={MemberProfile} />
why when I'm trigger:
window.location.href = `/client/${id}/members/${mid}`;
then my url is changed to correct form like in the route path but not redirect me to MemberProfile component?
Thanks for any help!
as
<Route path="/client/:id/members" component={Members} />
declared before
<Route path="/client/:id/members/:mid" component={MemberProfile} />
and
/client/${id}/members/${mid}
fit
"/client/:id/members"
Members component will still be rendered.
Consider one of the following:
decleare MemberProfile before Members
change MembersProfile route to /client/:id/member/:mid for example
use exact Route property
Given:
<Route path="/client/:id/members" component={Members} />
<Route path="/client/:id/members/:mid" component={MemberProfile} />
It seems like you are rendering these routes into a Switch component. Remember that the Switch component renders the first child <Route> or <Redirect> that matches the location. This means that route path order and specificity matters.
Order your routes by decreasing specificity. "/client/:id/members/:mid" is more specific than "/client/:id/members" and should render higher in the Switch.
<Switch>
...
<Route path="/client/:id/members/:mid" component={MemberProfile} />
<Route path="/client/:id/members" component={Members} />
...
</Switch>
Additional note
You may want to avoid using window.location.href to redirect as this reloads the entire page, and thus, your app. Use a Redirect component to render a declarative navigation, or use history.replace(`/client/${id}/members/${mid}`); to issue an imperative redirect.
I'm using React Router v5 and I ran into problems when trying to define routes split across multiple components. The case I tried is something like the following:
<Switch>
<MyRoutes />
<Route path="/foo">
<Foo />
</Route>
</Switch>
with the MyRoutes component looking like
<>
<Route path="/bar">
<Bar />
</Route>
<Route path="/baz">
<Baz />
</Route>
</>
The problem now is that routes declared after the custom MyRoutes component don't work, the custom component seems to match the route without rendering anything. The routes inside it do actually work as expected.
The reason I'm splitting the routes like this, apart from organizing related routes together is that I also need the separate routing component as a standalone to be able to integrate a set of components into a legacy web application. And I'd like to avoid duplicating the routes here for that purpose.
But obviously I'm doing something that you're not supposed to do with React Router, though I'm not entirely sure why this is an issue. So I would like to understand the limitation here a bit more to avoid running into variations of this again in the future. Why can't I split Routes like this into separate components within a single Switch, what are the exact limitations here?
Is putting a separate Switch into each component the right answer here? This does seem wrong from a conceptual point of view as each Route is supposed to be exclusive, only one of them should ever be rendered. Multiple Switch components would allow multiple components to be rendered at the same time, even though in practice this should not happen as the routes should be exclusive and not overlap.
What is the proper, intended way to have modular Routes in React Router v5 and not a single huge Routing component?
Issue
Routers inclusively render all matching routes whereas the Switch component exclusively renders the first match it finds. It's not strictly enforced but the only valid children of the Switch component are the Route and Redirect components. If you render any other component inside the Switch it will be returned and rendered and route matching completes, any routes after won't be reachable.
Switch
Renders the first child <Route> or <Redirect> that matches the
location.
How is this different than just using a bunch of <Route>s?
<Switch> is unique in that it renders a route exclusively. In
contrast, every <Route> that matches the location renders
inclusively.
Solution
Render MyRoutes in a route on its own so the Switch component can manage path matching and rendering. I suggest using path="/" so the nested routes can also further be matched.
<Switch>
<Route path="/foo">
<Foo />
</Route>
<Route
path="/" // <-- allows matching "/bar" and "/baz"
component={MyRoutes}
/>
</Switch>
MyRoutes
You will also want to render these routes into a Switch component. Any time you want to exclusively match routes you'll use the Switch component.
<Switch>
<Route path="/bar">
<Bar />
</Route>
<Route path="/baz">
<Baz />
</Route>
</Switch>
Also keep in mind that within the Switch component that path order and specificity matters. You will want to order the routes in inverse order of path specificity. This is to attempt to match and render more specific paths first, then falling back to less specific paths.
Example:
<Switch>
<Route path="/segment1/segment2/page" component={....} />
<Route path="/segment1/page" component={....} />
<Route path="/segment1" component={....} />
</Switch>
I am creating routing for my react app, could someone explain me difference between these two approaches.
From user point of view they work the same, what is the difference in performance, best practice?
First one is multiple Routes rendering different component for the same path:
<Route path='/:shop/booking' component={Services}/>
<Route path='/:shop/booking' component={Calendar}/>
Second is single path rendering components as props.children(?) :
<Route path='/:shop/booking'>
<Aux>
<Services/>
<Calendar/>
</Aux>
</Route>
<Route path='/'>
<Component>
</Route>
Is equivalent to :
<Route path='/' children={Component}/>
According to this : https://reacttraining.com/react-router/core/api/Route/children-func :
Sometimes you need to render whether the path matches the location or
not. In these cases, you can use the function children prop. It works
exactly like render except that it gets called whether there is a
match or not.The children render prop receives all the same route
props as the component and render methods, except when a route fails
to match the URL, then match is null. This allows you to dynamically
adjust your UI based on whether or not the route matches.
So by giving children prop instead of component to your route, you force it to render even if the current URL does not match. And I might be mistaking but it seems that adding a component prop to a route override its children prop.
Thus you cannot expect the same behavior for this two pieces of code :
<Route path='/:shop/booking' component={Services}/>
<Route path='/:shop/booking' component={Calendar}/>
Shows the two components for the specified path.
<Route path='/:shop/booking'>
<Aux>
<Services/>
<Calendar/>
</Aux>
</Route>
Shows the two components wrapped in another, for any path.
Finally, I would say that the best practice in React is to wrap your two components into one, and add it to the component prop of a route instead of creating two routes with the exact same path.
If you cannot wrap your two components because one has to be displayed on several routes, you can use something like the following :
<BrowserRouter>
<div>
<Header />
<Switch>
<Route path='/' component={Home}/>
<Route path='/foo' component={Foo}/>
<Route path='/foo2' component={Foo2}/>
</Switch>
<Footer />
</div>
</BrowserRouter>
I am using react-router to manage the routing of the app.
My app is divided into two panels, and I would like to route them independently. Like a change of route would change only one panel or the other.
I tried something like this, but if I change route from /conversations to /conversations/xxxxxx, it reloads the side component.
export default (
<div>
<Route path="login" component={Login} />
<Route path='/' component={requireAuthentication(Messenger)}>
<Route path='/conversations' components={{side: ConversationContainer, main: DefaultPanel}} />
<Route path='/conversations/:conversationId' components={{side: ConversationContainer, main: ActiveConversation}} />
<Route path='/ended-conversations' components={{side: EndedConversationContainer, main: DefaultPanel}} />
<Route path='/ended-conversations/:conversationId' components={{side: EndedConversationContainer, main: ActiveConversation}} />
<Redirect from="/" to="/conversations" />
</Route>
</div>
);
EDIT: For example, let's say /settings, I would want to changes the left panel without changing whatever is on the right to display the new component in place of ConversationContainer by example.
I hope that is a bit clear. Is there a way to do this with the router ?
Otherwise I will need to use a state probably.
Many thanks
React router helps you achieve this through nested routes. After configuring your routes, all that's needed is to access { this.props.children } in the render method of any routes that have nested routes in them. Exactly which child components will be passed to the component is determined by your route configuration.
// router.js
<Route path="conversations/:conversationid component={Conversation}> // ".../conversations/1234"
<Route path="began" component={BeginConversation} /> // ".../conversations/1234/began"
<Route path="ended" component={EndConversation} /> // ".../conversations/1234/ended"
</Route>
// Conversation.js
render() { // In the render method of the component matching the container route
<div>
<div className="left-panel">
// Format left panel... this will not change on route began/ended route change
</div>
{ this.props.children } // Tells react the render child components passed from your nested routes
</div>
Here are a couple useful resources!
React router docs & example on nested routes
A good thread on nested routes
This is how my Routing looks:
<Router history={createBrowserHistory()}>
<Route path="/" component={App}>
<IndexRoute component={EventAppContainer}/>
<Route path="fights" component={FightApp}/>
<Route path="fighters" component={FightersAppContainer}>
<Route path="/fighter/:id" component={FighterDetails} />
</Route>
</Route>
</Router>
In FightersAppContainer I fetch some data, if I render everything from there my nested route works perfectly. But If I want to have more children components, for example FightersAppContainer=>FightersApp=>FighterItem then I cannot reach my nested route. I do not get any error it just doesn't show up. How can I fix this?
To be more clear if I use <Link to={"/fighter/"+fighter.id}>{fighter.last_name}</Link> inside FightersAppContainer I achieve what I want. But if I try that inside for example FighterItem component (that is a child of FightersAppContainer component) then I don't get the desired result, as a matter of fact I don't get anything including errors...
The problem is that this:
<Route path="/fighter/:id" component={FighterDetails} />
being a child of the fighters path, would be expecting the route to be /fighters/fighter/:id, so your Link element would have to look like this if you're not on the fighters element itself:
<Link to={"/fighters/fighter/"+fighter.id}>{fighter.last_name}</Link>