T
TanStack4mo ago
conventional-tan

Hooks in shared component (useParams, useSearch)

Has anyone found a good solution for using type-safe hooks (useParams, useSearch) from components that could be mounted on multiple routes? I'm thinking of writing custom hooks that take an array of from instead of a single from string, but I'd love to know if someone has something better. The basic problem is something like this:
const Shared = () => {
const { id } = useParams({ from: '???' })
}

createFileRoute('/foo/$id')({
component: Shared
}}

createFileRoute('/bar/$id')({
component: Shared
}}
const Shared = () => {
const { id } = useParams({ from: '???' })
}

createFileRoute('/foo/$id')({
component: Shared
}}

createFileRoute('/bar/$id')({
component: Shared
}}
(Basically a component where the search params or path params are actually present in multiple routes, but just how do you get that in a type-safe way, i.e. without strict:false)
10 Replies
wise-white
wise-white4mo ago
pass in the id?
const Shared = (routeId: '/foo/$id' | '/bar/$id') => {
const { id } = useParams({ from: routeId })
}

createFileRoute('/foo/$id')({
component: () => <Shared routeId='/foo/$id' />
}}

createFileRoute('/bar/$id')({
component: () => <Shared routeId='/bar/$id' />
}}
const Shared = (routeId: '/foo/$id' | '/bar/$id') => {
const { id } = useParams({ from: routeId })
}

createFileRoute('/foo/$id')({
component: () => <Shared routeId='/foo/$id' />
}}

createFileRoute('/bar/$id')({
component: () => <Shared routeId='/bar/$id' />
}}
conventional-tan
conventional-tanOP4mo ago
ah ah so simple yet so good, thanks
wee-brown
wee-brown4mo ago
@Manuel Schiller Would you also suggest the same solution for children components? As I break components into smaller chunks I find myself wanting to use useLoaderData but it could be a different Router depending on where the child component is rendered simple example
export const Route = createFileRoute('/_auth/my-route')({
component: ParentComponent,
loader: // return stuff
});

function ParentComponent() {
return (
<div className="flex h-full flex-col overflow-visible">
<Suspense fallback={<div>Loading...</div>}>
<ChildComponent className="flex-1" />
</Suspense>
</div>
);
}
export const Route = createFileRoute('/_auth/my-route')({
component: ParentComponent,
loader: // return stuff
});

function ParentComponent() {
return (
<div className="flex h-full flex-col overflow-visible">
<Suspense fallback={<div>Loading...</div>}>
<ChildComponent className="flex-1" />
</Suspense>
</div>
);
}
export default function ChildComponent() {
const data = useLoaderData({ from: '...' });
export default function ChildComponent() {
const data = useLoaderData({ from: '...' });
A routerId prop would be pretty prolific through the codebase at that point I just found https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#consuming-data-from-loaders but you still have to pass the routeId to this getRouteApi function
wise-white
wise-white4mo ago
what is a routerId ?
wee-brown
wee-brown4mo ago
Sorry a routeId. In your example above you pass a routeId to the Shared component
wise-white
wise-white4mo ago
ah and you dont want to pass it around?
wee-brown
wee-brown4mo ago
I can, I'm just wondering if this is something you've seen done or if you know of a better way than every component that needs to use a Route hook having a routeId prop
wise-white
wise-white4mo ago
how else could it work? either name the route by id or use strict:false
wee-brown
wee-brown4mo ago
Could we use the last index in useMatches to determine where we are? That returns a routeId
wise-white
wise-white4mo ago
but it wouldn't be typed at compile time it's the same as using strict:false

Did you find this page helpful?