You may want to reuse options that are intended to be passed to Link, redirect or navigate. In which case you may decide an object literal is a good way to represent options passed to Link.
const dashboardLinkOptions = {
to: '/dashboard',
search: { search: '' },
}
function DashboardComponent() {
return <Link {...dashboardLinkOptions} />
}
const dashboardLinkOptions = {
to: '/dashboard',
search: { search: '' },
}
function DashboardComponent() {
return <Link {...dashboardLinkOptions} />
}
There are a few problems here. dashboardLinkOptions.to is inferred as string which by default will resolve to every route when passed to Link, navigate or redirect (this particular issue could be fixed by as const). The other issue here is we do not know dashboardLinkOptions even passes the type checker until it is spread into Link. We could very easily create incorrect navigation options and only when the options are spread into Link do we know there is a type error.
linkOptions is a function which type checks an object literal and returns the inferred input as is. This provides type safety on options exactly like Link before it is used allowing for easier maintenance and re-usability. Our above example using linkOptions looks like this:
const dashboardLinkOptions = linkOptions({
to: '/dashboard',
search: { search: '' },
})
function DashboardComponent() {
return <Link {...dashboardLinkOptions} />
}
const dashboardLinkOptions = linkOptions({
to: '/dashboard',
search: { search: '' },
})
function DashboardComponent() {
return <Link {...dashboardLinkOptions} />
}
This allows eager type checking of dashboardLinkOptions which can then be re-used anywhere
const dashboardLinkOptions = linkOptions({
to: '/dashboard',
search: { search: '' },
})
export const Route = createFileRoute('/dashboard')({
component: DashboardComponent,
validateSearch: (input) => ({ search: input.search }),
beforeLoad: () => {
// can used in redirect
throw redirect(dashboardLinkOptions)
},
})
function DashboardComponent() {
const navigate = useNavigate()
return (
<div>
{/** can be used in navigate */}
<button onClick={() => navigate(dashboardLinkOptions)} />
{/** can be used in Link */}
<Link {...dashboardLinkOptions} />
</div>
)
}
const dashboardLinkOptions = linkOptions({
to: '/dashboard',
search: { search: '' },
})
export const Route = createFileRoute('/dashboard')({
component: DashboardComponent,
validateSearch: (input) => ({ search: input.search }),
beforeLoad: () => {
// can used in redirect
throw redirect(dashboardLinkOptions)
},
})
function DashboardComponent() {
const navigate = useNavigate()
return (
<div>
{/** can be used in navigate */}
<button onClick={() => navigate(dashboardLinkOptions)} />
{/** can be used in Link */}
<Link {...dashboardLinkOptions} />
</div>
)
}
When creating navigation you might loop over an array to construct a navigation bar. In which case linkOptions can be called multiple times to construct an array which can be used to render an array of Link's
const options = [
linkOptions({
to: '/dashboard',
label: 'Summary',
activeOptions: { exact: true },
}),
linkOptions({
to: '/dashboard/invoices',
label: 'Invoices',
}),
linkOptions({
to: '/dashboard/users',
label: 'Users',
}),
]
function DashboardComponent() {
return (
<>
<div className="flex items-center border-b">
<h2 className="text-xl p-2">Dashboard</h2>
</div>
<div className="flex flex-wrap divide-x">
{options.map((option) => {
return (
<Link
{...option}
key={option.to}
activeProps={{ className: `font-bold` }}
className="p-2"
>
{option.label}
</Link>
)
})}
</div>
<hr />
<Outlet />
</>
)
}
const options = [
linkOptions({
to: '/dashboard',
label: 'Summary',
activeOptions: { exact: true },
}),
linkOptions({
to: '/dashboard/invoices',
label: 'Invoices',
}),
linkOptions({
to: '/dashboard/users',
label: 'Users',
}),
]
function DashboardComponent() {
return (
<>
<div className="flex items-center border-b">
<h2 className="text-xl p-2">Dashboard</h2>
</div>
<div className="flex flex-wrap divide-x">
{options.map((option) => {
return (
<Link
{...option}
key={option.to}
activeProps={{ className: `font-bold` }}
className="p-2"
>
{option.label}
</Link>
)
})}
</div>
<hr />
<Outlet />
</>
)
}
The input of linkOptions is inferred and returned, as shown with the use of label as this does not exist on Link props
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.