I am using @nextjs ~ 14.0.1
and next-ui
. I have a modal component where I have a form and I am using form action. Here's the modal component and action function's code ~
AddCandiateModal.tsx
⤵️
import React from "react";
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, useDisclosure, Input, Code, Select, SelectItem } from "@nextui-org/react";
import { PlusIcon } from "./PlusIcon";
import { useFormState, useFormStatus } from "react-dom";
import { useForm } from "react-hook-form";
import { createCandidateAction } from "@/app/actions/candidate-action";
import { CandidateStatusType } from "@/types/campaign";
function SubmitButton() {
const { pending } = useFormStatus()
return (
<Button color="primary" aria-disabled={pending} type="submit" isLoading={pending}>Save</Button>
)
}
export default function AddCandidateModal({ campaign_id, status }: { campaign_id: number, status: CandidateStatusType[] }) {
const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure();
const { register } = useForm()
const [state, formAction] = useFormState(createCandidateAction, null)
return (
<>
<Button onPress={onOpen} color="primary" endContent={<PlusIcon />}>Add New</Button>
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent>
{(onClose) => (
<>
<form
action={formAction}
>
<ModalHeader className="flex flex-col gap-1">New Candidate</ModalHeader>
<ModalBody>
<Select
items={status}
label="Candidate Status"
placeholder="Select a status"
className=""
isRequired
{...register('status')}
>
{(animal) => <SelectItem key={animal.id}>{animal.name}</SelectItem>}
</Select>
<div className="grid grid-cols-2 gap-3">
<Input isRequired label="First Name" placeholder="Travis" {...register('first_name')} />
<Input isRequired label="Last Name" placeholder="Scott" {...register('last_name')} />
</div>
<Input isRequired type="email" label="Email" placeholder="[email protected]" {...register('email')} />
<Input isRequired type="phone" label="Phone" placeholder="123 45678" {...register('phone_number')} />
{/* <label className="block mb-2 text-sm font-medium text-gray-900 dark:text-white" htmlFor="file_input">Upload Resume</label> */}
<span className="text-gray-500">Upload Resume</span>
<input className=" w-full py-3 px-3 text-gray-500 rounded-lg cursor-pointer bg-gray-100 " type="file" />
<input type="hidden" value={campaign_id} {...register("campaign")} />
</ModalBody>
<ModalFooter>
{state?.message && <Code>{state.message}</Code>}
<Button color="danger" variant="light" onPress={onClose}>
Close
</Button>
<SubmitButton />
</ModalFooter>
</form>
</>
)}
</ModalContent>
</Modal>
</>
);
}
candiate-action.ts
⤵️
'use server';
import { revalidatePath } from "next/cache";
import authSession from "@/utils/authsession";
import { redirect } from "next/navigation";
export async function createCandidateAction(prevState: any, formData: FormData ) {
const session = await authSession();
const campaign_id = parseInt(formData.get('campaign') as string);
console.log(prevState);
const data = {
campaign: formData.get('campaign'),
first_name: formData.get('first_name'),
last_name: formData.get('last_name'),
email: formData.get('email'),
phone_number: formData.get('phone_number'),
status: parseInt(formData.get('status') as string),
}
const response = await fetch(`${process.env.NEXTAUTH_BACKEND_URL}campaigns/${campaign_id}/candidate/`, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
'Authorization': `Token ${session.key}`,
}
});
if (response.ok) {
const candidate = await response.json();
revalidatePath(`/campaigns/${campaign_id}/candidates/`);
// redirect(`/campaigns/${campaign_id}/candidates/`);
return candidate;
} else {
const error = await response.json();
return {
message: error.detail,
};
}
}
I can't pass the onClose to the server component.
If I redirect from the action, the modal is still opened. Now, what should I do to close this modal? Thanks :)
you are using a specific library and its functions. we need to test the library to see the specific issue. if you track the
open
state withuseState
you are already using
useFormState
. insideuseEffect