shadcn-ui / ui

Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.
https://ui.shadcn.com
MIT License
67.12k stars 3.93k forks source link

[Bug]: Can't use custom validation with React Hook Form #3154

Open juliofortunato opened 5 months ago

juliofortunato commented 5 months ago

Describe the bug

I'm trying to use the rules prop from RHF's controller but it has no effect. I would like to use the validate property to add custom validation to a field.

<FormField
  control={form.control}
  name="endDate"
  rules={{
    validate: validateBooking,
  }}
  render={({ field }) => (
    <FormItem className="flex flex-col">
      <FormLabel>End date</FormLabel>
      <Popover>
        <PopoverTrigger asChild>
          <FormControl>
            <Button
              variant="outline"
              className={cn(
                "w-[240px] pl-3 text-left font-normal",
                !field.value && "text-muted-foreground"
              )}
            >
              {field.value ? (
                format(field.value, "PPP")
              ) : (
                <span>Pick a date</span>
              )}
              <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
            </Button>
          </FormControl>
        </PopoverTrigger>
        <PopoverContent className="w-auto p-0" align="start">
          <Calendar
            mode="single"
            selected={field.value}
            onSelect={field.onChange}
            initialFocus
          />
        </PopoverContent>
      </Popover>
      <FormDescription>
        The ending date for the booking.
      </FormDescription>
      <FormMessage />
    </FormItem>
  )}
/>

Affected component/components

FormField

How to reproduce

  1. Create a form using shadcn's <Form> components
  2. Try to set rules prop on a <FormItem>

Codesandbox/StackBlitz link

No response

Logs

No response

System Info

Chrome on Mac Os

Before submitting

ylyra commented 5 months ago

Try something like:

<FormField
  control={form.control}
  name="endDate"
  rules={{
    validate: (value) => {
      if (!value) {
        return 'End date is required'
      }

      // verify if the date is in the future
      if (value <= new Date()) {
        return 'End date must be in the future'
      }

      return true
    },
  }}
  render={({ field }) => (
    <FormItem className="flex flex-col">
      <FormLabel>End date</FormLabel>
      <Popover>
        <PopoverTrigger asChild>
          <FormControl>
            <Button
              variant="outline"
              className={cn(
                'w-[240px] pl-3 text-left font-normal',
                !field.value && 'text-muted-foreground',
              )}
            >
              {field.value ? (
                format(field.value, 'PPP')
              ) : (
                <span>Pick a date</span>
              )}
              <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
            </Button>
          </FormControl>
        </PopoverTrigger>
        <PopoverContent className="w-auto p-0" align="start">
          <Calendar
            mode="single"
            selected={field.value}
            onSelect={field.onChange}
            initialFocus
          />
        </PopoverContent>
      </Popover>
      <FormDescription>
        The ending date for the booking.
      </FormDescription>
      <FormMessage />
    </FormItem>
  )}
/>
juliofortunato commented 5 months ago

@ylyra didn't work unfortunately. I've add a console.log into the validate function like this, but it does nothing:

 <FormField
  control={form.control}
  name="endDate"
  rules={{
    validate: (value) => {
      console.log("hello");

      if (!value) {
        return "End date is required";
      }

      if (value <= new Date()) {
        return "End date must be in the future";
      }

      return true;
    },
  {...rest}
/>

I've also tried to change form's mode to 'onChange', but no luck either 😕

ylyra commented 5 months ago

@juliofortunato got the error here, I think it's a react-hook-form error, without the resolver inside the useForm my example works. You can try it here

federicogomezlara commented 2 months ago

I'm having the exact same issue, removing the resolver is not an option though, moreover I use the custom validation on a useFieldArray that gets control from useFormContext so that could be causing more issues. I tried using Controller from react-hook-form directly instead of FormField but it doesn't work either way

It turns out it's a React Hook Form thing, you can't use both the built in validator and a resolver

Re-validation of an input will only occur one field at time during a user's interaction. The lib itself will evaluate the error object to trigger a re-render accordingly. A resolver can not be used with the built-in validators (e.g.: required, min, etc.)