React Router DOM is rendering 2 components - javascript

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

Related

Browserrouting only running when displaying App

I build a website in react with Browserrouting and have the following code & issue
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App/>}>
<Route path="/home" element={<Homepage />} />
The Problem I have is when i run the App it opens the "App" and not the actual Homepage.
I tried to fix it by changing change the App to Homepage but it is not working.
In my understanding / needs to be there so the App displays in any other Path and is functioning.
Can some explain me, how i can make the path / still the Homepage without having to type /home to get there?
Thank you !
It appears you are rendering the Homepage in a nested route that is rendering App. In this configuration <Route path="/" element={<App />}> is what is called a Layout Route. App will be rendered when the path is exactly "/" and should render an Outlet component for the nested routes to render their content to.
An issue I see here, and you should have been seeing a react-router-dom invariant error regarding nesting an absolute path within a route rendering on an absolute path. In other words, "/home" is an absolute path and can't be nested under the absolute path "/"; it's not reachable.
If App really is a layout route and you always want it to render then you could remove the path prop from the parent route rendering App, and move Homepage to "/".
<BrowserRouter>
<Routes>
<Route element={<App />}>
<Route path="/" element={<Homepage />} />
</Route>
</Routes>
</BowserRouter>
or convert Homepage into an index route and leave the "/" path on the parent layout route so the relative routing can still work properly and Homepage is rendered on "/".
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Homepage />} />
</Route>
</Routes>
</BowserRouter>
I should point out that this is a slightly abnormal configuration. Typically the App component would render the Routes and Route components instead of an Outlet and it's assumed to all render on "/" by default. The Homepage component would then be rendered on path="/".
Example:
<BrowserRouter>
<App />
</BowserRouter>
...
const App = () => {
.... app logic ....
return (
<Routes>
<Route path="/" element={<Homepage />} />
</Routes>
);
};
There is OFC, the chance here that you didn't intend to nest these routes and want them to be matched and rendered independently. In this case the solution is to just unnest the Homepage route and continue using absolute paths for the root routes.
Example:
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/home" element={<Homepage />} />
</Routes>
</BowserRouter>
I found the issue
I had to
<Route path="" element={<App/>}>
<Route path="/" element={<Homepage />} />
Delete the Path / in App and only add Path / in Homepage.
It works now

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?

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.

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.

Categories