I have two <Route /> and I would like to render them based on the current location AND have a sliding transition when switching from one to the other.
My code is as follows but it doesn't work because the <Route /> elements are not rendered inside a <Routes /> element.
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import Container from '@mui/material/Container';
import SwipeableViews from 'react-swipeable-views';
function App() {
const location = useLocation();
const [value, setValue] = useState(location.pathname);
return (
<>
<Tabs
value={value}
onChange={(e, val) => setValue(val)}
indicatorColor="secondary"
variant="fullWidth"
textColor="inherit"
>
<Tab value="/" label="Reference" component={RouterLink} to="/" />
<Tab value="/quiz" label="Quiz" component={RouterLink} to="/quiz" />
</Tabs>
<Container>
{/* React wants this to be a <Routes /> but that would mean no transitions :( */}
<SwipeableViews>
<Route path="/" element={<div>reference</div>} />
<Route path="/quiz" element={<div>quiz</div>} />
</SwipeableViews>
</Container>
</>
);
}
To fix this issue I tried using react-swipeable-routes with provides a <SwipeableRoutes/> element which is a merge of <Routes /> and <SwipeableViews />. Unfortunately, this component is not compatible with v6 of React Router.
I tried to implement my own <SwipeableRoutes /> by inspecting the code of React Router v6, react-swipeable-views, and react-swipeable-routes. Here is the result I have arrived to:
import { Fragment } from 'react';
import {
createRoutesFromChildren,
matchRoutes,
RoutesProps,
useLocation,
} from 'react-router-dom';
import SwipeableViews from 'react-swipeable-views';
function SwipeableRoutes({ children, location }: RoutesProps) {
location = location || useLocation(); // Location
const routes = createRoutesFromChildren(children); // RouteObject[]
const matches = matchRoutes(routes, location); // RouteMatch[]
const index = routes.findIndex((route) => route === matches?.[0].route);
// index + 1 because index equals -1 in case of no matching route.
return (
<SwipeableViews index={index + 1}>
<div>No match!</div>
{routes.map((route) => (
<Fragment key={route.path}>{route.element}</Fragment>
))}
</SwipeableViews>
);
}
export default SwipeableRoutes;
My code works but I am not statisfied with it. How can I improve my component/rewrite it entirely to make react-swipeable-views compatible with React Router v6?
My problems are:
First, matchRoutes returns an array of matches, whereas <Routes/> only matches and renders one component. Is this safe?
Second, I use .findIndex() which has O(n) time complexity. Can I do better?
Third, will my component behave correctly when containing or by contained by nested <Route/> or <Routes/>?
Fourth, I fear my component does not fully implement all the lesser known behaviors and features of regular <Routes/> and is overall a hack, instead of a well thought-out React component.
I am making a small react project with Material UI and React Router.
I want to implement a tab system with transitions following the documentation. To do so, I use
react-swipeable-views
.I have two
<Route />
and I would like to render them based on the current location AND have a sliding transition when switching from one to the other.My code is as follows but it doesn't work because the
<Route />
elements are not rendered inside a<Routes />
element.To fix this issue I tried using
react-swipeable-routes
with provides a<SwipeableRoutes/>
element which is a merge of<Routes />
and<SwipeableViews />
. Unfortunately, this component is not compatible with v6 of React Router.I tried to implement my own
<SwipeableRoutes />
by inspecting the code of React Router v6,react-swipeable-views
, andreact-swipeable-routes
. Here is the result I have arrived to:My code works but I am not statisfied with it. How can I improve my component/rewrite it entirely to make
react-swipeable-views
compatible with React Router v6?My problems are:
First,
matchRoutes
returns an array of matches, whereas<Routes/>
only matches and renders one component. Is this safe?Second, I use
.findIndex()
which hasO(n)
time complexity. Can I do better? Third, will my component behave correctly when containing or by contained by nested<Route/>
or<Routes/>
?Fourth, I fear my component does not fully implement all the lesser known behaviors and features of regular
<Routes/>
and is overall a hack, instead of a well thought-out React component.Thank you in advance.