I have a simple route system, where I have a page as a Container (Home Page) and inside it I have two sections (NewPage and StoredPage).
I'm leaving all the route settings in one place, which is in App.tsx. I do this to stay centralized and have better control (I don't know if I'm doing it wrong).
The problem that happens is that, when changing the section, it is blinking, for some reason it is reloading the page again.
NOTE: I use react-router-guards
APP.TSX
import HomePage from 'pages/HomePage';
import StoredPage from 'pages/StoredPage';
import { memo, useMemo } from 'react';
import { BrowserRouter, Switch, Redirect } from 'react-router-dom';
import { GuardedRoute, GuardProvider, Next } from 'react-router-guards';
import { GuardFunctionRouteProps, GuardToRoute } from 'react-router-guards/dist/types';
export const Main = memo(() => {
const RequireLoginGuard = (to: GuardToRoute, from: GuardFunctionRouteProps | null, next: Next) => next();
return (
<ErrorBoundary instance={instance}>
<BrowserRouter>
<GuardProvider guards={[RequireLoginGuard]} loading={() => <div>Loading Global</div>}>
<Switch>
<GuardedRoute exact path='/new' loading={() => <div>Loading New Page</div>} component={HomePage} />
<GuardedRoute exact path='/stored' loading={() => <div>Loading Stored Page</div>} component={HomePage} />
<GuardedRoute path='*' component={() => <Redirect to='/new' />} />
</Switch>
</GuardProvider>
</BrowserRouter>
</ErrorBoundary>
);
});
HOMEPAGE.TSX
import NewPage from 'pages/NewPage';
import StoredPage from 'pages/StoredPage';
import { useMemo } from 'react';
import { Route, Router, Switch } from 'react-router-dom';
import SectionTitle from 'xpi-black-ui/Common/SectionTitle';
import Navigation from '../../components/Navigation';
import MainContainer, { SectionContainer } from './styles';
const HomePage = () => {
const navigationItems = useMemo(
() => [
{
key: 'new',
title: 'New Quotation',
link: '/new',
component: () => <NewPage />,
},
{
key: 'stored',
title: 'New Quotation 2',
link: '/stored',
component: () => <StoredPage />,
},
],
[]
);
return (
<MainContainer>
<SectionContainer>Aaaaaaaaaaaaaaa</SectionContainer>
<SectionContainer isSecondary>
<SectionTitle>
<Navigation />
<div>C</div>
</SectionTitle>
<Switch>
{navigationItems.map(({ key, link, component: PageComponent }) => (
<Route key={key} path={link} component={PageComponent} exact />
))}
</Switch>
</SectionContainer>
</MainContainer>
);
};
NAVIGATION.TSX
import { useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import NavLink, { Container } from './styles';
const Navigation = () => {
const { pathname } = useLocation();
const isActive = useCallback((linkPathSelected) => pathname === linkPathSelected, [pathname]);
const links = [
{ key: 'new', to: '/new', title: 'Criar Cotação' },
{ key: 'stored', to: '/stored', title: 'Cotações Armazenadas' },
];
return (
<Container>
{links.map(({ key, to, title }) => (
<NavLink exact key={key} to={to} $isActive={isActive(to)}>
{title}
</NavLink>
))}
</Container>
);
};
From what I can tell, the issue is in your Homepage component with the way you define your navigationItems array's component properties combined with how the Route component treats the component prop when a function is passed to it.
const navigationItems = useMemo(
() => [
{
key: 'new',
title: 'New Quotation',
link: '/new',
component: () => <NewPage />, // <-- function
},
{
key: 'stored',
title: 'New Quotation 2',
link: '/stored',
component: () => <StoredPage />, // <-- function
},
],
[]
);
...
<Switch>
{navigationItems.map(({ key, link, component: PageComponent }) => (
<Route
key={key}
path={link}
component={PageComponent} // <-- function passed through
exact
/>
))}
</Switch>
Route component:
When you use
component(instead ofrenderorchildren, below) the router usesReact.createElementto create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use therenderor thechildrenprop (below).
The solution is to define the navigationItems object properties in terms of the Route component's props API. If you are passing components through that you want to me rendered on the component prop then they should be references to the React components instead of functions returning JSX.
const navigationItems = useMemo(
() => [
{
key: 'new',
title: 'New Quotation',
link: '/new',
component: NewPage,
},
{
key: 'stored',
title: 'New Quotation 2',
link: '/stored',
component: StoredPage,
},
],
[]
);
...
<Switch>
{navigationItems.map(({ key, link, ...props }) => (
<Route key={key} exact path={link} {...props} />
))}
</Switch>
You may want to do the same thing for the link property, renaming it to path so it aligns more with its usage.
const navigationItems = useMemo(
() => [
{
key: 'new',
title: 'New Quotation',
path: '/new',
component: NewPage,
},
{
key: 'stored',
title: 'New Quotation 2',
path: '/stored',
component: StoredPage,
},
],
[]
);
...
<Switch>
{navigationItems.map(({ key, ...props }) => (
<Route key={key} exact {...props} />
))}
</Switch>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With