As a newcomer to TypeScript and the T3 stack (React Query / Tanstack Query), I am facing an issue with typing companyId
as string
. I want to avoid having to type companyId as string
every time I use it in my code, but I'm struggling to find the best practice in this particular stack. Coming from plain JavaScript and useEffects for API calls, I may not be following the most efficient approach.
Note: The code snippet below is located at /pages/companies/[id].tsx
In my initial attempt, I encountered a "Rendered more hooks than during the previous render" error at "const { data: company} ...", which led me to rethink my strategy:
const CompanyPage: NextPage = () => {
const router = useRouter()
const companyId = router.query.id
if (!companyId || Array.isArray(companyId)) return <div>Loading...</div> // Must check for Array.isArray due to NextJS/Typescript bug
const { data: company } = api.companies.getSingleById.useQuery({companyId: companyId});
if (!company ) return <div>Loading...</div>
...
return (...)
I attempted another approach, but encountered issues with the type 'string | string[] | undefined' for the companyId variable sourced from router.query.id:
const CompanyPage: NextPage = () => {
const router = useRouter()
const companyId: string = router.query.id // Type 'string | string[] | undefined' is not assignable to type 'string'
const { data: company } = api.companies.getSingleById.useQuery({companyId: companyId});
if (!company ) return <div>Loading...</div>
...
return (...)
UPDATE:
I have made changes to the code that seem to work, although I'm unsure if it's the ideal solution. By using this method, I only need to specify companyId as string
once:
const CompanyPage: NextPage = () => {
const router = useRouter()
const companyId = router.query.id
const { data: company } = api.companies.getSingleById.useQuery({companyId: companyId as string});
if (!companyId || Array.isArray(companyId)) return <div>Loading...</div> // Must check for Array.isArray due to NextJS/Typescript bug
if (!company ) return <div>Loading...</div>
...
return (...)
ANSWER:
Credits to Fabio for providing the accepted answer.
On other routes, I typically destructure router.query into multiple variables. Here's an example based on the accepted answer:
const { companyId, locationId } = useMemo(() => ({
companyId: router.query?.companyId?.toString() ?? "",
locationId: router.query?.locationId?.toString() ?? "",
}), [router.query?.companyId, router.query?.locationId]);