T
TanStack5w ago
ambitious-aqua

Match active tab with router

const tabHeaders = [
{ value: 'expenses', title: 'Expenses' },
{ value: 'timesheet', title: 'Timesheet' },
];

const RequestTabs: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const navigate = useNavigate();

const matchExpenses = useMatch({ from: '/section/requests/expenses', shouldThrow: false });
const activeTab = matchExpenses ? 'expenses' : 'timesheet';

const handleTabChanged = (value: string) => {
navigate({ to: `/section/requests/${value}` });
};

return (
<TabPanel activeTab={activeTab} tabHeaders={tabHeaders} onTabChange={handleTabChanged}>
{children}
</TabPanel>
);
};
const tabHeaders = [
{ value: 'expenses', title: 'Expenses' },
{ value: 'timesheet', title: 'Timesheet' },
];

const RequestTabs: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const navigate = useNavigate();

const matchExpenses = useMatch({ from: '/section/requests/expenses', shouldThrow: false });
const activeTab = matchExpenses ? 'expenses' : 'timesheet';

const handleTabChanged = (value: string) => {
navigate({ to: `/section/requests/${value}` });
};

return (
<TabPanel activeTab={activeTab} tabHeaders={tabHeaders} onTabChange={handleTabChanged}>
{children}
</TabPanel>
);
};
I originally wrote this with only 2 tabs in mind, how would I do this with 3 in mind? I don’t watch to add a useMatch for each case so I’m wondering if there’s some hook or something I’m missing. Or maybe it’s just wrong structurally
2 Replies
fair-rose
fair-rose5w ago
If you don't want to hardcode multiple useMatch hooks because your content in the tabs is dynamic or something, I think you could do something like this:
const tabHeaders = [
{ value: 'expenses', title: 'Expenses' },
{ value: 'timesheet', title: 'Timesheet' },
{ value: 'invoices', title: 'Invoices' },
];

const RequestTabs: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const navigate = useNavigate();
const matches = useMatches();

// find the deepest matching route that matches one of the tab values
const activeTab =
tabHeaders.find(tab =>
matches.some(m => m.pathname === `/section/requests/${tab.value}`)
)?.value ?? 'expenses';

const handleTabChanged = (value: string) => {
navigate({ to: `/section/requests/${value}` });
};

return (
<TabPanel activeTab={activeTab} tabHeaders={tabHeaders} onTabChange={handleTabChanged}>
{children}
</TabPanel>
);
};
const tabHeaders = [
{ value: 'expenses', title: 'Expenses' },
{ value: 'timesheet', title: 'Timesheet' },
{ value: 'invoices', title: 'Invoices' },
];

const RequestTabs: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const navigate = useNavigate();
const matches = useMatches();

// find the deepest matching route that matches one of the tab values
const activeTab =
tabHeaders.find(tab =>
matches.some(m => m.pathname === `/section/requests/${tab.value}`)
)?.value ?? 'expenses';

const handleTabChanged = (value: string) => {
navigate({ to: `/section/requests/${value}` });
};

return (
<TabPanel activeTab={activeTab} tabHeaders={tabHeaders} onTabChange={handleTabChanged}>
{children}
</TabPanel>
);
};
I don't have a sandbox but maybe this gives you an idea? The useMatches hook returns all matching routes, so you could look for a match based on the active tab or fallback to expenses. That being said if you have a limited number of tabs and they are hardcoded, using a couple useMatch hooks seems like it's not an issue 🤷‍♂️
ambitious-aqua
ambitious-aquaOP4w ago
@kylegill (kg) thanks for the reply, I’ll let you know how it goes! Went with the couple of useMatch hooks 👍

Did you find this page helpful?