Support client-side routing for all components with href prop
Provide a general summary of the issue here
From documentation, only Link and MenuItem are supported. Seems to be because only those 2 are making use of handleLinkClick. Other components, such as Tab, ListBoxItem etc, should work similarly.
๐ค Expected Behavior?
Using TanStack Router's createLink to wrap components with href prop allows client-side routing. Haven't got a chance to test with other routers.
๐ฏ Current Behavior
Using TanStack Router's createLink to wrap components with href prop results in full page refresh.
๐ Possible Solution
No response
๐ฆ Context
No response
๐ฅ๏ธ Steps to Reproduce
https://react-spectrum.adobe.com/react-aria/routing.html#tanstack-router https://stackblitz.com/edit/tanstack-router-xkvvewjr?file=package.json,src%2Fmain.tsx&preset=node
Version
1.12.1
What browsers are you seeing the problem on?
Firefox
If other, please specify.
No response
What operating system are you using?
Windows 11
๐งข Your Company/Team
No response
๐ท Tracking Issue
No response
https://github.com/TanStack/router/pull/4757#issuecomment-3118988281 has some additional information on why this is a bit tricky. Does something like https://stackblitz.com/edit/tanstack-router-qcvtkzgf?file=package.json,src%2Fmain.tsx&preset=node work for you in the meantime?
thanks. that works for now.
Didn't want to create a new issue since this one is still open but I thought I'd raise that it's not possible to use createLink from Tanstack Router on Table Row components since the method overrides the render props of children with its own props to indicate if a link isActive.
You can see it happening here https://github.com/TanStack/router/blob/b5ac27b9e764f33c6168ffdf8acb02cb91a95e23/packages/react-router/src/link.tsx#L576
So it's not possible to do something like this now
const RowLink = createLink(Row)
<Table aria-label="Fan lists">
<TableHeader columns={columns}>{(column) => <Column {...column} />}</TableHeader>
<TableBody
>
<Collection items={items}>
{({ item }) => (
<RowLink
columns={columns}
to={appWorkspaceSubscribersFanListIndexRoute.to}
params={{ fanListId: Number(item.id) }}
>
{(column) => { // <------ THESE TYPES ARE NOW TS ROUTER INSTEAD OF COLUMN TYPES
switch (column.id) {
case "name":
return <Cell typography={"h5"}>{item.name}</Cell>
case "actions":
return (
<Cell className={TABLE_ACTIONS_CELL_CLASSNAMES}>
<ActionsCell id={Number(item.id)} />
</Cell>
)
default:
return <></>
}
}}
</RowLink>
)}
</Collection>
</TableBody>
</Table>
I (think) this is something that would have to be handled on RAC side...or maybe the request should be to allow passing existing render props on the TS Router side...I'm unsure
EDIT: I need to head to bed but the TS Router docs state you can create a wrapper to pass to createLink so my idea is to pass the column rendering as a prop instead of children, I am halfway there I think but not sure if there's a better approach...
interface MyRowProps<T extends object> extends RowProps<T> {
renderColumn: (column: T) => React.ReactNode
}
function RACRowLink({ children, renderColumn, ...props }: MyRowProps<ColumnProps>) {
return (
<Row {...props}>
{composeRenderProps(children, (children, props) => {
return <>{renderColumn(props)}</>
})}
</Row>
)
}
const RowLink = createLink(RACRowLink)
I am also noticing full page reloads when using ListBoxItem and createLink
@mattkinnersley check my comment for a workaround above in https://github.com/TanStack/router/pull/4757
also here is my completed workaround for getting RAC Row to work nice with TS Router createLink. Really not a fan of creating a render prop just for this, so if anyone has a better idea please let me know
// Except<T> is from type-fest npm package to remove a property
interface RowLinkProps<T extends object> extends Except<RowProps<T>, "children"> {
renderColumn: (column: T) => React.ReactElement
}
function RACRowLink({ renderColumn, ...props }: RowLinkProps<ColumnProps>) {
return <Row {...props}>{renderColumn}</Row>
}
const RowLink = createLink(RACRowLink)
you can then use like this
<RowLink
to={appWorkspaceSubscribersFanListIndexRoute.to}
params={{ fanListId: Number(item.id) }}
columns={columns}
renderColumn={(column) => {
switch (column.id) {
case "name":
return <Cell typography={"h5"}>{item.name}</Cell>
case "actions":
return (
<Cell className={TABLE_ACTIONS_CELL_CLASSNAMES}>
<ActionsCell id={Number(item.id)} />
</Cell>
)
default:
return <></>
}
}}
/>
Thanks for any help!
Just realized you can use <Collection> for the columns too but I think my original comment here is still worth taking a look at...anyways here's another workaround/solution for now
const RowLink = createLink(Row)
<Table aria-label="Fan lists">
<TableHeader columns={columns}>{(column) => <Column {...column} />}</TableHeader>
<TableBody
renderEmptyState={() => (
<TableEmptyState
title="No Fan Lists Found"
description="Fan Lists are used for Presave and Messaging Campaigns"
/>
)}
>
<Collection items={items}>
{({ item }) => (
<RowLink
to={"/app/workspace/subscribers/fan-list/$fanListId"}
params={{ fanListId: Number(item.id) }}
>
<Collection items={columns}>
{(column) => {
switch (column.id) {
case "name":
return <Cell typography={"h5"}>{item.name}</Cell>
case "actions":
return (
<Cell className={TABLE_ACTIONS_CELL_CLASSNAMES}>
<ActionsCell id={Number(item.id)} />
</Cell>
)
default:
return <></>
}
}}
</Collection>
</RowLink>
)}
</Collection>
<InfoPageInfiniteSearchTableLoadMore />
</TableBody>
</Table>