React Router v4 Route difference - reactjs

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>

Related

Redirecting to subpage with id

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.

Split Routes inside a single Switch into multiple components

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>

React Router v4 rendering component twice

Here is my Router Implementation
<BrowserRouter>
<div>
<Route exact path="/" component={ProfilesIndex} />
<Route exact path="/profiles" component={ProfilesIndex} />
<Route exact path="/profiles/new" component={ProfileEditor} />
<Route exact path="/profiles/:id" component={ProfileEditor} />
</div>
</BrowserRouter>
When I am browsing to /profiles/new path, it is rendering the ProfileEditor component twice. For every other route, it is working fine.
Can someone suggest how to fix this problem ?
I found the answer after browsing into multiple sections in Router Docs. Problem was it was matching multiple routes
When user opens /profiles/new it matches two routes
/routes/new
/routes/:id
Because :id is like a wildcard * and it also matches new word so both routes get matched and hence the component gets rendered twice.
The solution is to wrap the routes with Switch if you do not want to match multiple routes.
<BrowserRouter>
<Switch>
<Route exact path="/" component={ProfilesIndex} />
<Route exact path="/profiles" component={ProfilesIndex} />
<Route exact path="/profiles/new" component={ProfileEditor} />
<Route exact path="/profiles/:id" component={ProfileEditor} />
</Switch>
</BrowserRouter>
For anyone coming here from v6+
exact prop no longer exists, the paths are exact by default if they aren't appended a wildcard *
However I was still getting a double render. I ran a prod build and checked and the double render is gone so probably nothing to worry about - sometimes hooks run twice in development mode (I guess that's what's happening internally)
for me, this is because of React.StrictNode which is wrapped arround App component.
it intentionally double render components (only in development) to enforce you, not use side effects on some of your component's
lifecycle events.
the reason behind that is documented here

Custom prop children with react-router

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.

React application with independent component routing

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

Resources