Reputation: 2675
I have a page which has three routes. The 3rd route has a tab component which handles 3 sub routes. I am able to navigate to Route 3, but unable to view the tabs and unable to render the content under each tab.
Please advice.
This is my code:
import "./styles.scss";
import React, { useState } from "react";
import { Redirect, Route, Switch } from "react-router";
import { BrowserRouter, Link } from "react-router-dom";
import { Tab, Tabs } from "@blueprintjs/core";
const ComponentC1 = () => <p>Component C1</p>;
const ComponentC2 = () => <p>Component C2</p>;
const ComponentC3 = () => <p>Component C3</p>;
const componentCRoutes = [
{
label: "Component C - 1",
code: "subC1",
component: ComponentC1
},
{
label: "Component C - 2",
code: "subC2",
component: ComponentC2
},
{
label: "Component C - 3",
code: "subC3",
component: ComponentC3
}
];
const ComponentA = () => <p>Component A</p>;
const ComponentB = () => <p>Component B</p>;
const ComponentC = (props) => {
const [tabId, setTabId] = useState(componentCRoutes[0].label);
const handleTabChange = (tabId) => setTabId(tabId);
return (
<>
<p>Component C</p>
<Tabs onChange={handleTabChange} selectedTabId={tabId}>
{componentCRoutes.map((tab) => {
return (
<Tab
key={tab.code}
id={tab.label}
title={
<Link to={`/${props.match.url}/${tab.code}`}>{tab.label}</Link>
}
/>
);
})}
</Tabs>
{(() => {
const { component, code } = componentCRoutes.find(
(item) => item.label === tabId
);
return (
<Route path={`${props.match.url}/${code}`} component={component} />
);
})()}
<Route exact path={props.match.url}>
<Redirect to={`${props.match.url}/${componentCRoutes[0].code}`} />
</Route>
</>
);
};
const routes = [
{ label: "Component A", path: "/routeA", component: ComponentA },
{ label: "Component B", path: "/routeB", component: ComponentB },
{ label: "Component C", path: "/routeC", component: ComponentC }
];
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<BrowserRouter>
{routes.map((item) => (
<Link key={item.path} to={item.path} style={{ paddingRight: "10px" }}>
{item.label}
</Link>
))}
<Switch>
{routes.map((route) => {
return (
<Route
key={route.path}
exact
path={route.path}
component={route.component}
/>
);
})}
<Route exact path="/">
<Redirect to="/routeA" />
</Route>
</Switch>
</BrowserRouter>
</div>
);
}
This is my codesandbox link
Please advice.
Upvotes: 1
Views: 2822
Reputation: 19863
Tabs
in ComponentC
are not working correctly as React-Router Link
. It can be fixed using history.push
in Tab's onChange
handler.find
to define the Route
, that looks dirty. It can be fixed using a Switch
and Route
in nested component i.e. ComponentC
Route
and Redirect
to make default paths. That can be simplified as well.props.match.url
and props.match.path
incorrectly. props.match.url
(URL) should be used in Link
or history.push
and props.match.path
(PATH) should be used in path of your nested Routes declarations.After fixing all the issues mentioned above, Here is the working code:
(Also, note that the Route
that has nested routes should not be marked exact={true}
)
Main Routes:
const routes = [
{ exact: true, label: "Component A", path: "/routeA", component: ComponentA },
{ exact: true, label: "Component B", path: "/routeB", component: ComponentB }
{ exact: false, label: "Component C", path: "/routeC", component: ComponentC }
// ^ it is false because it has nested routes
];
// JSX
<BrowserRouter>
{routes.map((item) => (
<Link key={item.path} to={item.path}>
{item.label}
</Link>
))}
<Switch>
{routes.map((route) => {
return (
<Route
key={route.path}
exact={route.exact}
path={route.path}
component={route.component}
/>
);
})}
<Redirect exact from="/" to="/routeA" />
</Switch>
</BrowserRouter>
And Here is nested routes declarations inside ComponentC:
const routes = [
{
label: "Component C1",
code: "subC1",
component: ComponentC1
},
{
label: "Component C2",
code: "subC2",
component: ComponentC2
},
{
label: "Component C3",
code: "subC3",
component: ComponentC3
}
];
export default function ComponentC(props) {
const [tabId, setTabId] = useState(routes[0].code);
const handleTabChange = (tabId) => {
props.history.push(`${props.match.url}/${tabId}`);
setTabId(tabId);
};
return (
<>
<Tabs onChange={handleTabChange} selectedTabId={tabId}>
{routes.map((tab) => {
return <Tab key={tab.code} id={tab.code} title={tab.label} />;
})}
</Tabs>
<Switch>
{routes.map((route) => (
<Route
key={route.code}
exact
path={`${props.match.path}/${route.code}`}
component={route.component}
/>
))}
<Redirect
exact
from={props.match.url}
to={`${props.match.url}/${routes[0].code}`}
/>
</Switch>
</>
);
}
Here is full demo on Sandbox.
Upvotes: 2