I'm pretty new to TypeScript, as well as using the T3 stack (React Query / Tanstack Query). I'm trying to type companyId
as string
, so that I don't have to type companyId as string
every time I use it later on it in the code, but I can't figure out how to best to that or what the best practice is with this stack... I'm used to plain old JavaScript and useEffects to do API calls (and probably writing worse code).
Note: the following code exists at /pages/companies/[id].tsx
The following is my first attempt, but I get a "Rendered more hooks than during the previous render" error at "const { data: company} ...", which makes sense:
const CompanyPage: NextPage = () => {
const router = useRouter()
const companyId = router.query.id
if (!companyId || Array.isArray(companyId)) return <div>Loading...</div> // have to check for Array.isArray because of NextJS/Typescript bug
const { data: company } = api.companies.getSingleById.useQuery({companyId: companyId});
if (!company ) return <div>Loading...</div>
...
return (...)
I tried doing the following, but I was not allowed because the type for companyId from router.query.id is string | string[] | undefined
.
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 changed it to the following now, which seems to work, but it doesn't feel quite right it's the correct way to do things. (With this method, I only have to write companyId as string
once, which is fine.)
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> // have to check for Array.isArray because of NextJS/Typescript bug
if (!company ) return <div>Loading...</div>
...
return (...)
ANSWER:
Thank you to Fabio for the accepted answer.
I'm destructuring router.query into multiple variables on other routes, so this is an example of doing that 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]);