Stykgwar
Stykgwar

Reputation: 721

How can I disable next button if the user didn't complete the first step?

so I've created a multi-step form using shadcn , react-hook form and zod. I've created 3 types of zod schema and joined them using z.union from zod. The problem I'm encountering right now is I can still press the next button even though I didn't complete the input.

Appointment.ts

export const AppointmentSchema = z.union([
    FirstPartSchema,
    SecondPartSchema,
    ThirdPartSchema,
])

//There came from different Ts files
export const FirstPartSchema= z.object({
    title: z.string(),
  });

export const SecondPartSchema= z.object({
    name: z.string(),
  });


export const ThirdPartSchema= z.object({
    description: z.string(),
  });

This is my form.tsx.

const steps = [
  {
    id: 'Step 1',
    name: 'General Information',
    fields: ['title']
  },
  {
    id: 'Step 2',
    name: 'Date , Time and Assistance ',
    fields: ['name']
  },
  {
    id: "Step 3",
    name: "Dry Run",
    fields: ['description']
  },
]

const CreateScheduleDialog = ({ open, setOpen }: Props) => {

  const [step, setStep] = useState(0);
  const currentStep = steps[step];

  const nextStep = () => {
    if (step < steps.length - 1) {
      setStep(step + 1);
    }
  };

  const prevStep = () => {
    if (step > 0) {
      setStep(step - 1);
    }
  };

  const form = useForm<AppointmentSchemaType>({
    resolver: zodResolver(AppointmentSchema),
    defaultValues: {
    }
  })

  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button disabled={open !== false ? false : true} className='w-full' color="primary-foreground">Add Schedule</Button>
      </DialogTrigger>
      <DialogContent className={cn(`max-w-[400px] md:max-w-[800px]`)}>
        <DialogHeader>
          <DialogTitle>{currentStep.name}</DialogTitle>
          <DialogDescription>
            Step {step + 1} of {steps.length}
          </DialogDescription>
        </DialogHeader>
        <Form {...form}>
          {step == 0 && (
            <div className='flex flex-col gap-y-2'>
              <FormField
                control={form.control}
                name="title"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Title</FormLabel>
                    <FormControl>
                      <Input placeholder="Title" {...field} />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
            </div>
          )}
          {step == 1 && (
            <div>
              <FormField
                control={form.control}
                name="name"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Name</FormLabel>
                    <FormControl>
                      <Input placeholder="Name" {...field} />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
            </div>
          )}
          {step == 2 && (
            <div>
              <FormField
                control={form.control}
                name="description"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Description</FormLabel>
                    <FormControl>
                      <Input placeholder="Description" {...field} />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
            </div>
          )}
        </Form>
        <DialogFooter>
          <Button onClick={prevStep} disabled={step === 0}>Back</Button>
          {step === steps.length - 1 ? (
            <Button
            // onClick={form.handleSubmit(onSubmit)}
            >Submit</Button>
          ) : (
            <Button onClick={nextStep}>Next</Button>
          )}
        </DialogFooter>
      </DialogContent>

    </Dialog>
  )
}

Upvotes: 0

Views: 306

Answers (1)

Anil Singha
Anil Singha

Reputation: 438

First you can use this flag in your function

const nextStep = async () => {
    const isValid = await form.trigger(); // Trigger validation for all fields
    if (isValid) {
      setStep((prevStep) => prevStep + 1);
    }
  }

instead of doing this

<Button onClick={prevStep} disabled={step === 0}>
  Back
</Button>
{step === steps.length - 1 ? (
  <Button
    // onClick={form.handleSubmit(onSubmit)}
  >
    Submit
  </Button>
) : (
  <Button onClick={nextStep}>
    Next
  </Button>
)}

you can use this now

<Button onClick={prevStep} disabled={step === 0}>
      Back
    </Button>
    {step === steps.length - 1 ? (
      <Button onClick={form.handleSubmit(onSubmit)} disabled={!form.formState.isValid}>
        Submit
      </Button>
    ) : (
      <Button onClick={nextStep} disabled={!form.formState.isValid}>
        Next
      </Button>
    )}

Disable the "Next" button (disabled={!form.formState.isValid}) based on form.formState.isValid. This prevents the user from advancing to the next step if the current step's fields are invalid.

Upvotes: 1

Related Questions