javascript - Cum să faci o cerere API cu tRPC și NextJS fără o eroare de apel invalid de hook


Încerc să trimit datele introduse de utilizator către API-ul meu tRPC. Când încerc să trimit interogarea, primesc o eroare că pot folosi React Hooks doar într-un component funcțional. Cred că nu pot apela useQuery de la tRPC dintr-un callback pentru că este un hook de React, dar cum pot trimite mutația când formularul este completat?

componenta codului meu poștal

'use client';

import { type NextPage } from "next"
import { NextPageButtonLink } from "../UI/NextPageButtonLink"
import { api } from "../utils/api";
import { useState } from "react";


const ZipCode: NextPage = () => {
  const [zip code, setZipCode] = useState("");
  // This posts on every rerender and input. Ideally, it should only post when the user clicks submit
  // const postZipCodeResult = API.zipcode.postZipCode.use query({userId: "1", zipcode: zipcode});
  const handleSubmit = () => {
    // This throws the invalid hook location error
    const postZipCodeResult = API.zipcode.postZipCode.use query({userId: "1", zipcode: zipcode});

    console.log("Posting result", postZipCodeResult)
  }
  return (
    <div className="bg-[#3276AE] flex flex-col items-center h-screen">
       <form onSubmit={handleSubmit}>
        <label htmlFor="zipcode">Enter your zipcode:
        <input type="text" id="zipcode" name="zipcode" required value={zipcode} onChange={e => setZipCode(e.target.value)} />
        </label>
        <button type="submit">Submit</button>
      </form>
        <NextPageButtonLink pageName="survey" msg="Click here to start the demographics survey." />
    </div>
  )
}

export default ZipCode;

Pagina mea cu cod poștal:

import dynamic from "next/dynamic";

const ZipCode = dynamic(() => import('../components/zipcode'), {
  SSR: false
})

export default function ZipCodePage() {
  return (<ZipCode/>)
}


routerul meu de cod poștal

import { z } from "zod";

import { createTRPCRouter, publicProcedure } from "../trpc";

export const zipcodeRouter = createTRPCRouter({
  postZipCode: publicProcedure
    .input(z.object({ userId: z.string(), zipcode: z.string() }))
    .query(({ input }) => {

      return {
        zipcode: `User: ${input.userId} zipcode: ${input.zipcode}`,
      };
    }),

});

Răspuns #1

Nu poți apela hook-uri condiționat, dar poți dezactiva interogarea și apoi folosi refetch pentru a o declanșa când utilizatorul face clic pe buton.

const ZipCode: NextPage = () => {
  const [zipcode, setZipCode] = useState("");

  const { data, refetch } = api.zipcode.postZipCode.useQuery({userId: "1", zipcode: zipcode}, {
    enabled: false
  });

  const handleSubmit = () => {
    refetch();
  }

  return (
    <div className="bg-[#3276AE] flex flex-col items-center h-screen">
      <form onSubmit={handleSubmit}>
        <label htmlFor="zipcode">Enter your zipcode:
        <input type="text" id="zipcode" name="zipcode" required value={zipcode} onChange={e => setZipCode(e.target.value)} />
        </label>
        <button type="submit">Submit</button>
      </form>
      {
        data && (
          <pre>{data.zipcode}</pre>
        )
      }
    </div>
  )
}

export default ZipCode;