React application with independent component routing - reactjs

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

Related

How do i render child route components under a parent route in react router without routing to the child paths

<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="update-profile" element={<UpdateProfileForm />} />
<Route path="bookmarks" element={<Bookmarks />} />
</Route>
I have my routes setup like this, but i want all the child routes of dashboard to render under the same path without actually routing to the child routes
Example: The index route is dashboard Home component and it renders in /dashboard path
I want update-profile, bookmarks to render under /dashboard path .Not like switching to /dashboard/update-profile path in the browser.
If i click bookmarks in my dashboard navbar it should render the Bookmarks component but my path should be /dashboard in the browser itself, for now it is routing to the /dashboard/bookmarks.
How to overcome this?
As far as, I have understood you question, You just need to configure the parent component in routing. All the child components need not to be configured in the routing.
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
</Route>
The child components can be imported inside the parent component and used inside the parent component itself like below,
DashboardHome.jsx
import 'Bookmarkscomponent' from Bookmarkscomponent.jsx
import 'Slidercomponent' from Slidercomponent.jsx
export default function DashboardHome() {
return (
<>
<Slidercomponent />
<Bookmarkscomponent />
</>
)
}
Also, If you want to render the same /dashboard when you click the Bookmark. Then you can configure the same route in the link like below,
<Link to="/dashboard">Bookmark</Link>

React router does not render route when URL accessed directly

Consider this the main App component (imports left out for brevity):
const App = () => {
const [orderRoutes, setOrderRoutes] = useState([])
const updateOrderRoutes = (newRoute) => {
orderRoutes.push(newRoute)
setOrderRoutes(orderRoutes)
}
const renderedOrderRoutes = orderRoutes.map(route => {
return (
<Route
path={`/${route.class}/${route.order}`}
exact
key={`/${route.class}/${route.order}`}
>
<CatalogPage />
</Route>
)
})
return (
<BrowserRouter>
<Header
updateOrderRoutes={updateOrderRoutes}
/>
<Route path="/" exact component={Home} />
<Route path="/aboutus" exact component={AboutUs} />
<Route path="/faq" exact component={Faq} />
<Route path="/register" exact component={Register} />
{renderedOrderRoutes}
<Footer />
</BrowserRouter>
)
}
export default App
The challenge is that some of the routes are not known when rendering the initial App component. They will be known when an AJAX request in the <Header> component is responded to. The header will then update the new route to the orderRoutes state property, re-rendering the App component every time. The routes that are the result of the AJAX call (that is made in the <Header>) are then rendered to the <BrowserRouter> (in {renderedOrderRoutes}). In the <Header>, there is a <Link> for each route being rendered as a result of the same AJAX call, so that every menu entry (The <Link>s) will have a corresponding route.
This works fine, but when I access one of the URL's that this mechanism generates directly (e.g.: refresh the page), the <CatalogPage> component is not rendered.
So, for instance let's say that the AJAX call results in a bunch of routes and one of those is /t-shirts/tanktops. I will get a menu entry with a link to that path. When I click that menu entry the <CatalogPage> component is rendered. But when I access /t-shirts/tanktops directly, the <CatalogPage> component is not rendered.
How can I alter this code to make the URL's that are a result of the AJAX call directly accessible?
EDIT
OK, I 'solved' this (don't like it) by forcing the <App> component to re-render when one of the <Link>s was clicked by creating an unused piece of state on the App component called activeOrderRoute. I passed the setter down to the Header as a prop and connected it as a callback to the onClick handler for each Link that was created in response to the AJAX request. This essentially forces the App to re-render and render the routes, which solved my problems.
Still, that does not seem like the correct way to do it so any help would be appreciated.
React router does not directly have routing support for all URLs. It catches the default domain only the remaining routing is done on client side and requests are not served.
If your domain is www.mydomain.com, you can not access the URL www.mydomain.com/info directly in the react router.
Solutions:
You can use a hash router but that makes the URLs unfriendly for SEO
You can set up a catch-all routes and route it yourself
This link would help you with the same
https://ui.dev/react-router-cannot-get-url-refresh/
you need to modify your webpack.config.js and add the following lines.
module.exports = {
devServer: {
historyApiFallback: true,
},
...
Instead of trying to explicitly render a route for each asynchronously fetched route, leverage the power of react-router-dom and render a dynamic route path string that can handle any catalog page.
Instead of this:
const renderedOrderRoutes = orderRoutes.map(route => {
return (
<Route
path={`/${route.class}/${route.order}`}
exact
key={`/${route.class}/${route.order}`}
>
<CatalogPage />
</Route>
)
})
return (
<BrowserRouter>
<Header
updateOrderRoutes={updateOrderRoutes}
/>
<Route path="/" exact component={Home} />
<Route path="/aboutus" exact component={AboutUs} />
<Route path="/faq" exact component={Faq} />
<Route path="/register" exact component={Register} />
{renderedOrderRoutes}
<Footer />
</BrowserRouter>
)
Render a single dynamic route in your Router. Use a Switch so only a single route component is matched and rendered. Reorder the routes so the more specific paths can be matched before less specific paths. Now, when a URL has a path that is of the shape "/someClass/someOrder" it can be matched before you try matching any of the more general paths. You will see that the home path ("/") is matched last and the reordering allows us to remove the exact prop on all routes.
return (
<BrowserRouter>
<Header updateOrderRoutes={updateOrderRoutes} />
<Switch>
<Route
path="/:class/:order"
exact
component={CatalogPage}
/>
<Route path="/aboutus" component={AboutUs} />
<Route path="/faq" component={Faq} />
<Route path="/register" component={Register} />
<Route path="/" component={Home} />
</Switch>
<Footer />
</BrowserRouter>
)
You may need to adjust some logic in CatalogPage to handle possible undefined catalog data, whatever it is using from the route props/etc... to render catalog stuff.
In your Header component make the asynchronous call there to fetch the routes that can be navigated to so you can dynamically render render links to them (if that is even why you are passing the routes to Header).

React Router v4 Route difference

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>

Multiple matching in react-router or ambiguous routing

I have a task to build routing which maintains 2 type of components: sidebar and content. If the url contains category-:type I have to render Sidebar component and if url contains any content type like profile, about or seller I have to render proper content.
If create <Route /> for each combination of sidebar and content type there will be a lot of items.
How can I build routing for this purpose?
As I know I can't use routing like <Route path="/**/:profile" component={Profile}> because if Router will match this path it will stop and avoid other comparison.
Here is my current routing
const history = syncHistoryWithStore(browserHistory, routing);
ReactDOM.render(
<Router history={history}>
<Route path="/" component={Base}>
<IndexRedirect to="signin" />
<Route path="n=:id/:title" component={Item} />
<Route path="search(/:type)" component={require_auth(Search)} />
<Route path="people(/:type)" component={require_auth(People_Layout)} />
<Route path="person/:id" component={require_auth(Person_Scene_Layout)} />
<Route path="signin" component={Signin} />
<Route path="signup" component={Signup} />
<Route path="profile" component={require_auth(Profile)} />
</Route>
</Router>
, document.querySelector('#appRoot')
);
So, I have to extend this code to allow navigation on sidebar at the same time. I need to preserve current routing and add routing for matching Sidebar, something like <Route path="category-:type/n=:id/:title" component={Item} />. This routing can render both <Sidebar/> and <Item/> components but to make this work with all other routing I have to double almost all existing routes.
So, If I understand your question correctly, you have the requirement to Render
components dynamically based on the Router Params like
Navigation Component - Some Sidebar Navigation
Content Components - profile, about , seller etc.
So, you can not directly filter the Components and Inject in Router.
But what you can do is basically
Initiate a Parent-Components on any Router navigation by using path="/*"
and Inside the Parent-Component that, check for the Value of Router Pamas / Queries by
this.props.location.query.yourParamName
and based on that, inject you Child-Component i.e. Navigation or Content.
<Router history={hashHistory} >
<Route path="/*" component='ParentComponent'/>
</Router>
export default class CartItem extends React.Component {
render() {
// check for Router Params and decide the Child Componenton on fly using any conditional statement.
// var Component = this.props.location.query.yourParamName
return (
<div className='parent-wrapper'>
React.createElement(Component, props, ...children)
</div>
);
}
}

react router parameter loading index route only

So I have my routes defines as follows:
<Route path="/manage" component={Manage}>
<IndexRoute component={Manage}></IndexRoute>
<Route path=":id" component={Edit}></Route>
</Route>
</Route>
Now when I click on a button in my Manage component I call following function:
handleEditClick(e) {
e.preventDefault();
let selectedId= this.state.selectedId;
this.props.router.replace("/manage/" + selectedId);
},
My browser does display me the correct link but my component is not loaded as should. It only renders me the Manage component and not the Edit component.
Am I missing something here?
UPDATE
changing the child route to <Route path="/manage/:id" component={Edit}></Route> also loads me the Manage component
UPDATE 2
if I do not use child routes but in stead create them on the same level, the Edit component does render, but I'd like to use child routes.
On your render() you need a {this.props.children} ... it's where React router knows where to put child Component
In your routes
<Route path="/manage" component={Manage}>
<IndexRoute component={Manage}></IndexRoute>
<Route path=":id" component={Edit}></Route>
</Route>
If you navigate to /manage/:id, React Router renders Manager and Edit components... but where to put Edit?
So
you need to have something like this.
class Manager extends Component {
render(){
return (
<div>
Hello
{this.props.children}
</div>
);
}
}
So React Router knows to put Edit along side when you go to /manage/:id
<div>
Hello
<Edit />
</div>

Resources