What is the best way to convert the NextJS router.query.id to a string?

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]);

Answer №1

To handle query parameters efficiently, you can utilize the concept of optional chaining and nullish coalescing along with the query params' toString() method. Here is a sample based on your code snippet:

const CompanyPage: NextPage = () => {
  const router = useRouter();

  // this result will be of type string
  const companyId = useMemo(() => {
    return router.query?.id?.toString?.() ?? "";
  }, [router.query?.id]);

  const { data: company } = api.companies.getSingleById.useQuery({
    companyId: companyId,
  });

  if (!company) return <div>Loading...</div>;

  return; // ...
};

The reason behind the query parameters being of type string | string[] is because having a catch-all segment would split each URL part by / and present it as an array.

An Illustrative Example

Consider having the route /some-segment/[...href] and the URL as /some-segment/200/my-post, the following scenario would apply:

  • The contents of the href query variable would be ["200", "my-post"]
  • Calling href?.toString?.() would give you /some-segment/200/my-post.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the solution for addressing the deprecation warning "The 'importsNotUsedAsValues' flag will no longer work in TypeScript 5.5"?

Is anyone familiar with how to resolve this tsconfig error? The flag 'importsNotUsedAsValues' is outdated and will no longer work in TypeScript 5.5. To address this error, use 'ignoreDeprecations: "5.0"' or switch to using & ...

Strategies for resolving type issues in NextJs with Typescript

In my project using Next.js with TypeScript, I encountered an issue while trying to utilize the skipLibCheck = false property for enhanced checking. This additional check caused the build process to break, resulting in the following error: Error info - U ...

Cannot locate module: Issue: Unable to locate 'pnpapi' in '/app/node_modules/next/dist/lib'

While working on my next.js app, I ran into a critical error that caused it to break. Error message: Module not found: Error: Can't resolve 'pnpapi' in '/app/node_modules/next/dist/lib' > This is a build error that occurred dur ...

Collaborating on data through module federation

Currently, I am in the process of developing a Vue.js and TypeScript application using Vite. In addition, I am utilizing the vite-module-federation-plugin for sharing code across different applications. My main inquiry revolves around whether it is possibl ...

Using the `import.meta` meta-property is restricted to the `es2020`, `esnext`, or `system` options in snowpack+testing-library and will result in an error

I've been encountering issues while setting up Jest and React Testing Library for the snowpack project. The error message I'm receiving is "The 'import.meta' meta-property is only allowed when the '--module' option is 'es ...

Update name of an angular 2 component template

Is it possible to dynamically change the component template <FAQ-omni></FAQ-omni> based on a click event in the list? <div class="row"> <div class="col-xlg-4 col-xl-12 col-lg-12 col-md-7 col-sm-12 col-xs-12" title="FAQ" baCard ...

Can we verify if this API response is accurate?

I am currently delving into the world of API's and developing a basic response for users when they hit an endpoint on my express app. One question that has been lingering in my mind is what constitutes a proper API response – must it always be an o ...

List out the decorators

One query is bothering me - I am attempting to create my own version of Injectable and I need to determine if a specific decorator exists in my Class. Is there a way to list all decorators of a class? Let's take the example below. All I want to know i ...

Angular - The filter method cannot be used on Observables

I have a database with users who need to complete unique homework sessions. Each homework session has its own identifier, name, date, and other details. After fetching all the user's homework from the database, it is stored in a private variable call ...

Encountering a problem with the navigation bar in the latest version of Next.js, version 13

"use client" import {Navbar, Button, Link, Text} from "@nextui-org/react"; import {styled} from "@nextui-org/react" const Box = styled("div", { boxSizing: "border-box", }); const AcmeLogo = () => ( ...

Steps for importing a CommonJS module with module.exports in Typescript

When working with ES5 code, I encountered an error that I cannot seem to resolve. Despite following the language spec and checking my TypeScript version 1.7.5, I still can't figure out why this error is occurring. Error TS2349: Cannot invoke an expre ...

Implementing Angular - Injecting a component dynamically into another component

Currently, I am working on developing a small UI components framework for my personal use and enjoyment. One of the components I'm working on is a Tab component. To test this component, I need to dynamically inject another component (TabContainerCompo ...

What is the best way to generate a type that generates a dot notation of nested class properties as string literals?

In relation to the AWS SDK, there are various clients with namespaces and properties within each one. The library exports AWS, containing clients like DynamoDB and ACM. The DynamoDB client has a property named DocumentClient, while ACM has a property call ...

Using static methods within a static class to achieve method overloading in Typescript

I have a TypeScript static class that converts key-value pairs to strings. The values can be boolean, number, or string, but I want them all to end up as strings with specific implementations. [{ key: "key1", value: false }, { key: "key2&qu ...

The issue arises when attempting to utilize ExpressJS middleware in conjunction with NextJS Link feature

Incorporating Next with Express routes, I have set up a scenario where /a should only be accessible to authorized individuals, while /b is open to the public. ... other imports... const app = next({ isDev }) const handle = app.getRequestHandler() async f ...

Angular StrictNullChecks: "Error - object may be null"

I am encountering an issue with the 'strictNullChecks' setting in my Angular project. This has resulted in numerous errors across my templates (.html), such as: <input #inputValue type="text" (keyup.ent ...

The properties in Typescript, specifically 'value', are not compatible with each other

I've encountered an error while working on a TypeScript project with React Context. The error message states: Argument of type 'Context<{}>' is not assignable to parameter of type 'Context<IsProductContext>'. The type ...

Guide to integrating a native web component into Vue3

After creating the renderComponent method to display a webcomponent, I attempted to use it in my componentView.vue file. export function renderComponent(el: Element, component: Component,props: VNodeProps,appContext: AppContext){ let vnode: VNode | undefin ...

Alert an Angular 2 component about changes triggered by a service

Currently working with Angular 2 and utilizing an AuthService for authentication. I am at a crossroads on how to effectively inform other components when a user logs in or out. Seeking advice on the best approach for managing this situation. Any insights ...

Angular2, multi-functional overlay element that can be integrated with all components throughout the application

These are the two components I have: overlay @Component({ selector: 'overlay', template: '<div class="check"><ng-content></ng-content></div>' }) export class Overlay { save(params) { //bunch ...