using same component for different route path in react-router v4 - javascript

I am trying to have separate routes but same component for add/edit forms in my react app like the below:
<Switch>
<Route exact path="/dashboard" component={Dashboard}></Route>
<Route exact path="/clients" component={Clients}></Route>
<Route exact path="/add-client" component={manageClient}></Route>
<Route exact path="/edit-client" component={manageClient}></Route>
<Route component={ NotFound } />
</Switch>
Now in the manageClient component, I parse the query params (I pass in a query string with client id in edit route), I render conditionally based on the query param passed.
The problem is that this doesn't remount the whole component again. Say an edit page is opened, and the user clicks on add component, the URL changes, but the component doesn't reload and hence remains on the edit page.
Is there a way to handle this?

Using different key for each route should force components to rebuild:
<Route
key="add-client"
exact path="/add-client"
component={manageClient}
/>
<Route
key="edit-client"
exact path="/edit-client"
component={manageClient}
/>

One solution is use inline function with component, that will render a new component each time, But this is not a good idea.
Like this:
<Route exact path="/add-client" component={props => <ManageClient {...props} />}></Route>
<Route exact path="/edit-client" component={props => <ManageClient {...props} />}></Route>
Better solution would be, use componentWillReceiveProps lifecycle method in ManageClient component. Idea is whenever we render same component for two routes and switching between them, then react will not unmount-mount component, it will basically update the component only. So if you are making any api call or require some data do all in this method on route change.
To check, use this code and see it will get called on route change.
componentWillReceiveProps(nextProps){
console.log('route chnaged')
}
Note: Put the condition and make the api call only when route changes.

<Route exact path={["/add-client", "/edit-client"]}>
<manageClient />
</Route>
Reference
Version 5.2.0
https://reacttraining.com/react-router/web/api/Route/path-string-string

My problem was we used an common path in-between, which causes dynamic path to not working
<Switch>
<Route key="Home" path="/home" component={Home} />
<Route key="PolicyPlan-create" path="/PolicyPlan/create" component={PolicyPlanCreatePage} />
{/* <Route key="PolicyPlan-list" path="/PolicyPlan" component={PolicyPlanListPage} /> */}
<Route key="PolicyPlan-list" path="/PolicyPlan/list" component={PolicyPlanListPage} />
<Route key="PolicyPlan-edit" path="/PolicyPlan/edit/:id" component={PolicyPlanCreatePage} />
<Route key="cardDesign" path="/cardDesign" component={cardDesign} />
<Route key="Admin-create" path="/admin/create" component={RegisterPage} />
</Switch>
So don't use the path like the commented one, now the code is working
.................
this.props.history.push("/PolicyPlan/edit/" + row.PolicyPlanId);
.............

You can simply provide an array of paths in a single route tag as follows -
<Route exact path={["/add-client", "/edit-client"]} component={manageClient}></Route>

Related

What is the difference between <Route /> vs <Route> </Route> in React?

I am using the react-router-dom and creating some routes in my application. Can anyone please explain me the specific usages of and . What will be the difference affect in the rendering if there are any. I will include a sample code snippet.
import { BrowserRouter as Router, Route } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Router>
<Route path="/home" component={App} />
<Route path='/about'>
<About />
</Route>
</Router>
);
I tried both and both are working fine. But I was unable to find the specific usage of one over other.
The react-router-dom#5 Route component has 4 ways to render content. The primary method is to directly render content as the children prop. Note here that no route props will be passed to the component.
<Route path='/about'>
<About />
</Route>
The other 3 ways are to use one of the route render methods.
The recommended method of rendering something with a <Route> is to use
children elements, as shown above. There are, however, a few other
methods you can use to render something with a <Route>. These are
provided mostly for supporting apps that were built with earlier
versions of the router before hooks were introduced.
<Route component>
<Route render>
<Route children> function
Examples:
component - Other common method, route props are implicitly passed as props to the component.
<Route path='/about' component={About} />
render function - Alternative to pass along runtime props as well as the route props.
<Route path='/about' render={routeProps => <About {...routeProps} {...otherProps} />} />
children function - Renders regardless of route match and passes route props to component to handle any conditional logic.
<Route path='/about' children={routeProps => <About {...routeProps} />} />
Just a small add to Mr. Drew Reese's answer, the way rendering a component directly inside <Route> allow you to freely pass your own props inside your component (it's more familiar to the way we usually do with normal React components).
Ex: You have a page About, inside, it includes 3 tabs: "About me", "About my team", "About my blog".
const About = () => {
// call APIs, handle...
return (
<>
<Switch>
<Route path='/about-me'>
<Me data={dataMe} />
</Route>
<Route path='/about-team'>
<Team data={dataTeam} />
</Route>
<Route path='/about-blog'>
<Blog data={dataBlog} />
</Route>
</Switch>
</>
)
}

Render multiple elements in React Router v6.+

I need the answer to this question: Render multiple components in React Router but for the newer version of react-router-dom (I'm using v6.0.2)
the older version of router-dom would work like this:
<Route path="/">
<Header/>
<Home/>
</Route>
while the new one looks like this:
<Route exact path="/" element={<Home/>}/>
I'm not sure how to add the Header as well
Try wrap them in a fragment
<Route exact path="/" element={<><Header/><Home/></>}/>
I ran in to the same issue, but also needed one page to get data from the parent element.
<Route path="/" element={<><Header/><Home children={<HomeDataProvider/>}/></>} />
try this if u want to add two or more component just put them inside of fragment tag
<Route exact path="/" element={<></>}/>
for Render multiple components in React Router for the newer version of react-router-dom (I'm using v6.5.0)
<Route path="/" element={
<>
< AddTodo addTodo={ addTodo }/>
<Todos todos={todos} onDelete={onDelete}/>
</>}>
</Route>

Navigating between multiple params - React js

So I've been working on a project lately using React js (I thought it would be similar to React native), while I pretty much understand most of it as I previously worked with React native a lot. There are still some new things I'm learning for example the react-router-dom npm. So I understand the basics and how it works, but I'm trying to use parameters which change depending on the user (User ID).
The code below shows how I'm currently using my router. While going to home (/) and /user/:id works, I can't go to /user/:id/settings. If I try going to the settings it renders both the /user/:id page and below it renders settings page.
What I want to be able to do is if the user is in the user/:id page they can click a button which takes them to the user/:id/settings instead of the current issue where it renders the setting page below the user page.
App.jsx
export class App extends React.Component {
render() {
return (
<Router>
<Route exact path="/" component={Home} />
<Route path="/user/:id" component={User} />
<Route path="/user/:id/settings" component={Setting} />
</Router>
)
}
};
User.jsx
render() {
return (
<div>
{/* Cool information about the user */}
<div
className="optionContent"
onClick={() => {
let uri = `/user/${this.props.match.params.id}/settings`;
this.props.history.push(uri)
}}
>
Press me
</div>
</div>
);
}
Extra information:
I have tried using variable parameters for users but I wasn't able to full make those work as once the user enters /user/:id page the buttons update the url but not the parameters in this.
I need to have the ID within the url to fetch from the API and some other stuff
Variable url: /user/:id/:type?
This is because with React Router v5 which is currently the latest version as v6 is completed, the routes aren't exact by default which means that for each of the routes, if the current route starts with the route of a component, this component will be displayed.
For your example:
<Router>
<Route exact path="/" component={Home} />
<Route path="/user/:id" component={User} />
<Route path="/user/:id/settings" component={Setting} />
</Router>
If the current route is "/user/user1" then it only matches the User component.
If the current route is "/user/user1/settings/ then it matches User and Settings components so they will both be rendered as you are finding.
To fix it, simply use the exact keyword on the component with the fewer requirements.
<Router>
<Route exact path="/" component={Home} />
<Route exact path="/user/:id" component={User} />
<Route path="/user/:id/settings" component={Setting} />
</Router>

Is it possible to use MemoryRouter nested in BrowserRouter in React?

I have been searching, and trying for a while now, however I couldn't find an answer if it is possible to use MemoryRouter for only specific routes while I use BrowserRouter in general. I wan t to navigate to a certain component but not change the url, tried it like so, but it changes the url but not rendering the component, the complete opposite what I wish.
<BrowserRouter>
<Switch>
<Route path="/login" component={Login} exact />
<Route path="/" component={MainPage} />
<MemoryRouter>
<Route
path='/somecomponent'
component={SomeComponent}
/>
</MemoryRouter>
</Switch>
</BrowserRouter>
Routes inside the MemoryRouter are relative to the MemoryRouter, not to your current location as displayed in the URL bar.
As far as it's concerned, the current route is "/", and it will only render components at <Route path="/">. If you were to add
<Route path="/">
<Redirect to="/somecomponent" />
</Route>
directly under MemoryRouter it should go to the path you're looking for, and render as intended.

How to use React Prompt with nested Routers?

Let's say I have 2 very simple Routers. Each of them is a separate component.
First one is the ParentRouter:
<TopComponent></TopComponent>
<Router>
<Switch>
<Route path="/child" component={ChildRouter}>
</Route>
<Route path="/outside" component={OutsideComponent}>
</Route>
</Switch>
</Router>
Second one is the ChildRouter:
<Router>
<Switch>
<Route exact path="/child" component={ListOfExampleComponents}>
</Route>
<Route path="/child/example" component={ExampleComponent}>
</Route>
</Switch>
</Router>
TopComponent is displayed all the time on every URL.
When I am on /child/example I want to have a React Prompt displayed if I click a Link to the /outside or to the /child (the Link can be placed inside ExampleComponent or TopComponent) or use back button to go back to the /child.
How to do it?
Ok, I found out what is the answer.
I added Prompt as it is intended in the Parent Router and deleted <Router> tags in the Child Router.

Categories