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

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.

Related

React router v4 - Browser router not rendering parent component

Currently I'm trying to make it so that my parent router is activated each time. I'm not very sure of the terminology here.
<BrowserRouter>
<Switch>
<Route exact path='/browserRouterPath' component={loginComponent}/>
<HashRouter>
<Route path='/hashRouterPath' component={loginComponent2}/>
<Route path='/' component={loggedInComponent}/>
</HashRouter>
</Switch>
</BrowserRouter>
LoggedInComponent
<Switch>
<Route exact path='/' render={() => (
<Redirect to: {{pathname: '/finalComponent'> }}/>
)}> />
...
</Switch>
My current problem is that my components in the hashRouter are not being called.
When the loggedInComponent redirects it to the new path, the hashRouter is not called.
However, if I remove the hashRouter in the first block of code. The hashRouter is called without incident, but that breaks the CSS/styling of the components in the program.
Is there a way to render the parent component each time? Or is there a workaround that eliminates the hashRouter but keeps the CSS for me?

React Router DOM is rendering 2 components

This is my router structure:
<Router>
<main className="py-3">
<Switch>
<Container>
<Route exact path="/admin" component={AdminScreen}></Route>
<Route path="/:campaignId" component={CampaignScreen}></Route>
<Route path="/" component={HomeScreen} exact></Route>
</Container>
</Switch>
</main>
</Router>
For "some" reason, when I go to /admin i also get the CampaignScreen rendered.
I added the <Switch> as you can see, but it does not seem to help.
Where lies the problem?
Since admin can theoretically also be a campaignId react router thinks it matches both routes, and you have the <Container> element directly inside the Switch it still renders both, the solution is to have your routes directly in the Switch

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.

React router remounts children while changing routes (without reconciliation)

I have a situation where few routes consists of the same component and I want to preserve its state, but with this behavior it's fairly impossible.
Live example (look at seconds elapsed):
https://csb-43lp1km647-nkiinaibit.now.sh
CodeSandbox:
https://codesandbox.io/s/43lp1km647
Of course I could save and restore state of timer while changing routes, but my app has an infinite CSS transition as a background (not on every route).
I was trying adding keys, even portal, but it still remounts everything.
Is there any way to enforce typical React's reconciliation (remount only elements that have to be remounted)?
Update:
Just found out that <Switch /> will render everything until it finds element that matches, and that could be either a certain <Route /> or any element except it's inside <Fragment />.
Code:
<Router history={history}>
<Switch>
<Route path="/third" component={RouteWithoutTimer} />
<React.Fragment>
<Timer />
<Route exact path="/" component={FirstRoute} />
<Route path="/second" component={SecondRoute} />
</React.Fragment>
</Switch>
</Router>
Or with wildcard route:
<Router history={history}>
<Switch>
<Route path="/third" component={RouteWithoutTimer} />
<React.Fragment>
<Timer />
<Switch>
<Route exact path="/" component={FirstRoute} />
<Route component={NotFoundRoute} />
</Switch>
</React.Fragment>
</Switch>
</Router>
It's not a solution I was looking for, but it works fine.
You are mounting a new instance of Timer in each route.
If you want to share a component across routes, you should lift it to the parent component of the routes:
<React.Fragment>
<Timer />
<Switch>
<Route exact path="/" component={FirstRoute} />
<Route path="/second" component={SecondRoute} />
</Switch>
</React.Fragment>
This is where the state management libraries come to scene.
In React, states are per component and has lifecycle of it's owned component, so when router destructs/remounts the component, you lose all the data. I advice you to migrate your state to a state management library like redux as soon as possible. Otherwise your code can be mess if this is a medium-sized application.

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

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>

Categories