add virtual layout route when configuration is not found
Describe the bug
I get an uncaught runtime error when I try to use the useSearch hook.
I was trying to move my state managed filters to search parameters so it would be easier for users to share filtered contents. To do this, I followed the instractions provided on the official Tanstack-Router guide.
Here is the code I have used to create my search parameters:
type TInstituteSearch = {
page?: number;
pageSize?: number;
searchTerm?: string;
sortColumn?: SortingState;
filters?: TInstituteFilters;
};
export const Route = createFileRoute('/_authenticated/_institutes/institutes')({
validateSearch: (search: Record<string, unknown>): TInstituteSearch => {
return {
page: search.page as number,
pageSize: search.pageSize as number,
searchTerm: search.searchTerm as string,
sortColumn: search.sortColumn as SortingState,
filters: search.filters as TInstituteFilters,
};
},
beforeLoad: ({ context }) => {
const user = context.AuthenticationContext.user;
assignPermissions(user!, '/institutes');
},
component: Institutes,
});
Then I am simply trying to get the parameter values from the parameters. I have tried doing this in two ways:
First way:
import { getRouteApi } from '@tanstack/react-router';
const Route = getRouteApi('/_authenticated/_institutes/institutes');
const Institutes = () => {
const { page, pageSize, searchTerm, sortColumn, filters } = Route.useSearch();
return <div>......</div>
}
Second way:
import { useSearch } from '@tanstack/react-router';
const Institutes = () => {
const { page, pageSize, searchTerm, sortColumn, filters } = useSearch({
from: '/_authenticated/_institutes/institutes',
});
return <div>......</div>
}
Both of these methods causes the Cannot read properties of undefined (reading 'routeId') error:
The only way to not get this error is to no include the from property in the useSearch hook but they I get a typescript error saying Expected 1 arguments, but got 0.. I don't know what causes this.
Your Example Website or App
https://stackblitz.com/edit/github-fyoeqy?file=src%2Fpages%2FInstitute.tsx
Steps to Reproduce the Bug or Issue
Click on the institutes link in the navbar. In this example I get a different error which says that Invariant failed: Could not find an active match from "/_auth/_institutes/institutes" even though it exists on the routetree.
Here is the generated routeTree.gen.ts file on my local problem:
/* prettier-ignore-start */
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file is auto-generated by TanStack Router
// Import Routes
import { Route as rootRoute } from './routes/__root'
import { Route as LoginImport } from './routes/login'
import { Route as ForgotPasswordImport } from './routes/forgotPassword'
import { Route as ErrorImport } from './routes/error'
import { Route as AuthenticatedImport } from './routes/_authenticated'
import { Route as AuthenticatedIndexImport } from './routes/_authenticated/index'
import { Route as AuthenticatedManagementChipTagTemplatesImport } from './routes/_authenticated/management/chipTagTemplates'
import { Route as AuthenticatedKeysProductKeysImport } from './routes/_authenticated/_keys/productKeys'
import { Route as AuthenticatedKeysIssuerKeysImport } from './routes/_authenticated/_keys/issuerKeys'
import { Route as AuthenticatedKeysInstituteKeysImport } from './routes/_authenticated/_keys/instituteKeys'
import { Route as AuthenticatedIssuersIssuersImport } from './routes/_authenticated/_issuers/issuers'
import { Route as AuthenticatedIssuersIssuerImport } from './routes/_authenticated/_issuers/issuer'
import { Route as AuthenticatedInstitutesInstitutionImport } from './routes/_authenticated/_institutes/institution'
import { Route as AuthenticatedInstitutesInstitutesImport } from './routes/_authenticated/_institutes/institutes'
// Create/Update Routes
const LoginRoute = LoginImport.update({
path: '/login',
getParentRoute: () => rootRoute,
} as any)
const ForgotPasswordRoute = ForgotPasswordImport.update({
path: '/forgotPassword',
getParentRoute: () => rootRoute,
} as any)
const ErrorRoute = ErrorImport.update({
path: '/error',
getParentRoute: () => rootRoute,
} as any)
const AuthenticatedRoute = AuthenticatedImport.update({
id: '/_authenticated',
getParentRoute: () => rootRoute,
} as any)
const AuthenticatedIndexRoute = AuthenticatedIndexImport.update({
path: '/',
getParentRoute: () => AuthenticatedRoute,
} as any)
const AuthenticatedManagementChipTagTemplatesRoute =
AuthenticatedManagementChipTagTemplatesImport.update({
path: '/management/chipTagTemplates',
getParentRoute: () => AuthenticatedRoute,
} as any)
const AuthenticatedKeysProductKeysRoute =
AuthenticatedKeysProductKeysImport.update({
path: '/productKeys',
getParentRoute: () => AuthenticatedRoute,
} as any)
const AuthenticatedKeysIssuerKeysRoute =
AuthenticatedKeysIssuerKeysImport.update({
path: '/issuerKeys',
getParentRoute: () => AuthenticatedRoute,
} as any)
const AuthenticatedKeysInstituteKeysRoute =
AuthenticatedKeysInstituteKeysImport.update({
path: '/instituteKeys',
getParentRoute: () => AuthenticatedRoute,
} as any)
const AuthenticatedIssuersIssuersRoute =
AuthenticatedIssuersIssuersImport.update({
path: '/issuers',
getParentRoute: () => AuthenticatedRoute,
} as any)
const AuthenticatedIssuersIssuerRoute = AuthenticatedIssuersIssuerImport.update(
{
path: '/issuer',
getParentRoute: () => AuthenticatedRoute,
} as any,
)
const AuthenticatedInstitutesInstitutionRoute =
AuthenticatedInstitutesInstitutionImport.update({
path: '/institution',
getParentRoute: () => AuthenticatedRoute,
} as any)
const AuthenticatedInstitutesInstitutesRoute =
AuthenticatedInstitutesInstitutesImport.update({
path: '/institutes',
getParentRoute: () => AuthenticatedRoute,
} as any)
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/_authenticated': {
preLoaderRoute: typeof AuthenticatedImport
parentRoute: typeof rootRoute
}
'/error': {
preLoaderRoute: typeof ErrorImport
parentRoute: typeof rootRoute
}
'/forgotPassword': {
preLoaderRoute: typeof ForgotPasswordImport
parentRoute: typeof rootRoute
}
'/login': {
preLoaderRoute: typeof LoginImport
parentRoute: typeof rootRoute
}
'/_authenticated/': {
preLoaderRoute: typeof AuthenticatedIndexImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/_institutes/institutes': {
preLoaderRoute: typeof AuthenticatedInstitutesInstitutesImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/_institutes/institution': {
preLoaderRoute: typeof AuthenticatedInstitutesInstitutionImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/_issuers/issuer': {
preLoaderRoute: typeof AuthenticatedIssuersIssuerImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/_issuers/issuers': {
preLoaderRoute: typeof AuthenticatedIssuersIssuersImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/_keys/instituteKeys': {
preLoaderRoute: typeof AuthenticatedKeysInstituteKeysImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/_keys/issuerKeys': {
preLoaderRoute: typeof AuthenticatedKeysIssuerKeysImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/_keys/productKeys': {
preLoaderRoute: typeof AuthenticatedKeysProductKeysImport
parentRoute: typeof AuthenticatedImport
}
'/_authenticated/management/chipTagTemplates': {
preLoaderRoute: typeof AuthenticatedManagementChipTagTemplatesImport
parentRoute: typeof AuthenticatedImport
}
}
}
// Create and export the route tree
export const routeTree = rootRoute.addChildren([
AuthenticatedRoute.addChildren([
AuthenticatedIndexRoute,
AuthenticatedInstitutesInstitutesRoute,
AuthenticatedInstitutesInstitutionRoute,
AuthenticatedIssuersIssuerRoute,
AuthenticatedIssuersIssuersRoute,
AuthenticatedKeysInstituteKeysRoute,
AuthenticatedKeysIssuerKeysRoute,
AuthenticatedKeysProductKeysRoute,
AuthenticatedManagementChipTagTemplatesRoute,
]),
ErrorRoute,
ForgotPasswordRoute,
LoginRoute,
])
/* prettier-ignore-end */
Expected behavior
Should route to the page without causing the runtime error.
Screenshots or Videos
No response
Platform
- OS: Windows
- Browser: Chrome
- Version: 126.0.6478.127
Additional context
This is my route definitions:
The only solution I have found so far is to set the strict property of the useSearch hook to false:
import { TInstituteSearchParams} from '../../types/institutes';
const { page, pageSize, searchTerm, sortColumn, filters } = useSearch({
strict: false,
}) as TInstituteSearch;
I don't know if there is a better fix for this. From what I understand, it is caused by the nested /_authenticated/_institutes/institute routing. When I try to specify the from property of the useSearch hook in a root route like /error, it works without an error.
When using a pathless/layout route, you need to have a configuration file for that route.
Here's the fixed reproduction (note, the _auth.tsx and _institutes.tsx files).
https://stackblitz.com/edit/github-fyoeqy-jkyv4p?file=src%2Froutes%2Findex.tsx
Speaking with @schiller-manuel, we came to the conclusion that the router-generator should create a virtual layout route in the generated route tree.
For consideration, if a user has a layout route identifier with no critical or non-critical configuration files, we should probably console.warn the user that they are using the layout route setting without any actual configuration.
Thank you for your help Sean, the problem is definitely fixed on my side.
I've read the docs but I don't remember reading the layout routes requiring a config file. A warning in the console would help a lot to devs encountering the same problem in the future.
Cannot reproduce with v1.91.3 anymore!
https://stackblitz.com/edit/github-fyoeqy-m7uijhqr?file=src%2Fpages%2FInstitute.tsx
@schiller-manuel