I'm creating a next js 14 project with react hook form/zod + react query and also using next js api route handlers as my backend plus I'm using next auth to Google auth but I think that my current issue is not about next auth
So I have a RSC component called CreatePetPage:
import FormAuthCreatePet from "@/components/FormAuthCreatePet";
export default async function CreatePetPage() {
return (
<main className="flex w-full h-full">
<div className="h-screen w-full">
<div className="w-full h-20 flex items-center px-4
border-b border-b-slate-300">
<h3 className="text-brand-secondary text-2xl">New Pet</h3>
</div>
<div className="w-full h-[calc(100%-5rem)] flex items-center justify-center">
<FormAuthCreatePet/>
</div>
</div>
</main>
)
}
and inside FormAuthCreatePet component that is an client component i have:
"use client"
import { ImSpinner9 } from "react-icons/im";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import * as z from "zod";
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { addPet } from "@/utils/actions/AddPet";
export async function addPet(dataForm:CreatePetSchema){
await fetch("http://localhost:3000/api/pets/create",{
method:"POST",
headers:{
"Content-Type":"application/json"
},
body: JSON.stringify(dataForm)
})
}
const createPetSchema = z.object({
name: z.string(),
age: z.coerce.number()
});
type CreatePetSchema = z.infer<typeof createPetSchema>;
export default function FormAuthCreatePet() {
const {handleSubmit,register,formState:{isSubmitting}} = useForm<CreatePetSchema>({
resolver: zodResolver(createPetSchema)
})
const queryClient = useQueryClient()
const {data:session} = useSession()
const router = useRouter()
const {mutateAsync:createPet} = useMutation({
mutationFn: addPet,
onSuccess: () =>{
queryClient.invalidateQueries({queryKey:['pets']})
}
})
async function OnSubmit(data:CreatePetSchema){
const dataForm = {...data,userEmail:session?.user?.email}
try {
await createPet(dataForm)
router.push("/dashboard")
} catch (error) {
console.log(error);
}
}
return (
<div className='h-[90%] w-[90%] '>
<form action="" className='flex flex-col justify-center items-center h-full gap-6'
onSubmit={handleSubmit(OnSubmit)}>
<label htmlFor="name">Pet Name</label>
<input className='py-1 bg-slate-200'
id='name'
{...register("name",{required:true})}
/>
<label htmlFor="age">Age</label>
<input className='py-1 bg-slate-200'
id='age'
{...register("age",{required:true})}
/>
<button type='submit'
disabled={isSubmitting}
className='text-brand-secondary font-semibold px-3 py-1 bg-blue-100 opacity-80
disabled:bg-slate-300 disabled:px-4 disabled:py-2'> {isSubmitting ?<ImSpinner9/> : "Done"}
</button>
</form>
</div>
)
}
So, in this component above in my mind, it should invalidate queryKey "pets", right? But in my project where I fetch my pets, it is not refetching my data that it is supposed to do because my query is invalid, right?
So here is where I fetch my "pets" inside PetCard(client component) which is a component in PetsPage rsc
PetsPage:
import PetCard from "@/components/petCard";
import { getAllPets } from "@/utils/actions/GetAllPets";
import { dehydrate,HydrationBoundary,QueryClient } from "@tanstack/react-query";
export async function getAllPets (){
const session = await auth()
const resp = await fetch("http://localhost:3000/api/pets",{
headers:{
'session': JSON.stringify(session)
}
})
const data = await resp.json()
return data
}
export default async function PetsPage() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery({
queryKey:['pets'],
queryFn: getAllPets
})
return (
<main className="h-screen w-full flex justify-center items-center ">
<HydrationBoundary state={dehydrate(queryClient)}>
<PetCard/>
</HydrationBoundary>
</main>
);
}
PetCard component:
"use client"
import { Pet } from "@/app/(auth)/dashboard/page";
import { getAllPets } from "@/utils/actions/GetAllPets";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
async function deletePet(petId:string){
await fetch(`http://localhost:3000/api/pets/${petId}`,{
method:"DELETE"
})
}
export default function PetCard() {
const queryClient = useQueryClient()
const { data } = useQuery({
queryKey: ['pets'],
queryFn: getAllPets
})
const {mutate:removePetById} = useMutation({
mutationFn: deletePet,
onSuccess: () => {
queryClient.invalidateQueries({queryKey:['pets']})
console.log("it runs!");
}
})
async function handleRemovePet(petId:string){
try {
removePetById(petId)
} catch (error) {
console.log(error);
}
}
return (
<div className="h-[70%] w-[70%] flex gap-10 items-center justify-center ">
{
data.map((pet:Pet) =>(
<div className="flex-1 flex-wrap relative p-4 bg-brand-third text-white" key={pet.id}>
<button className="absolute top-0 right-0 hover:bg-slate-200 p-2 hover:text-red-500"
onClick={() => handleRemovePet(pet.id)}>X</button>
<p>Pet Name: {pet.name}</p>
<p>Pet age: {pet.age}</p>
</div>
))
}
</div>
)
}
So, as I said, when I click and trigger handleRemovePet inside my database using my API route handler from next js the pet is being removed but it's not reflecting in my UI, so it is not invalidating my query and refreshing the data. Please help me, as I said in my mind this should been working just fine!
I tried to change the code many times but as I said in my mind, it should work just fine.