Inserting Shadcn Slider/DatePicker into react-hook-form
I could not find an example of using slider and date-picker in react-hook-form in Shadcn examples. So they need to be added in a custom way. Let’s start with slider example.
Slider
import { useEffect } from "react";
import { Slider } from "@/components/ui/slider";
type Props = {
value: number;
onChange: (value: number) => void;
};
export default function Ratio({ value, onChange }: Props) {
useEffect(() => {
if (!value) {
onChange(0.5);
}
}, [onChange, value]);
return (
<Slider
min={0}
max={1}
defaultValue={[value ?? 0.5]}
step={0.01}
onValueChange={(vals) => onChange(vals[0])}
/>
);
}
And inside your form file you need to use it like below:
<FormField
control={form.control}
name="slider"
render={({ field, formState: { errors } }) => (
<FormItem>
<FormLabel>Slider</FormLabel>
<FormControl>
<Ratio
value={field.value}
onChange={(value) => {
form.setValue("slider", value);
}}
/>
</FormControl>
</FormItem>
)}
/>
DatePicker
This one is a little bit more complex. I keep my dateRange in two separate properties in an object.
First, you need to create a custom date picker component. You can copy-paste this from shadcn offical examples. I am going to skip this part. Then you need to create a wrapper component for this date picker.
import { useState } from "react";
import { DateRange } from "react-day-picker";
import DateRangePicker from "@/components/templates/DateRangePicker";
type Props = {
formValue: {
startDate: string;
endDate: string;
};
currentGroup: "groupA" | "groupB";
setValuesDate: (
group: "groupA" | "groupB",
value: DateRange | undefined
) => void;
};
export default function TestDate({
formValue,
currentGroup,
setValuesDate,
}: Props) {
const [date, setDate] = useState<DateRange | undefined>({
from:
formValue && formValue.startDate
? new Date(formValue.startDate)
: undefined,
to:
formValue && formValue.endDate ? new Date(formValue.endDate) : undefined,
});
function handleSelectDate(selected: DateRange | undefined) {
setDate(selected);
setValuesDate(currentGroup, selected);
}
return (
<DateRangePicker
date={date}
setDate={handleSelectDate}
fromDate={new Date(Date.now())}
fullWidth
/>
);
}
And inside your form file you need to use it like below:
<FormField
control={form.control}
name={`redirects.groupA.dateRange`}
render={({ field: { value }, formState: { errors } }) => (
<FormItem className="w-full sm:flex-1">
<FormLabel>Group A Date Range</FormLabel>
<div>
<TestDate
formValue={value}
currentGroup="groupA"
setValuesDate={setValuesDate}
/>
</div>
<Form>
<FormMessage>
{/* your error path here */}
</FormMessage>
</FormItem>
)}
/>
Finally, you need to create a function to set the values of the date picker.
const setValuesDate = useCallback((value: DateRange | undefined) => {
if (!value || !value.from) {
form.setValue("dateRange", {
startDate: "1970-01-01",
endDate: "1970-01-01",
});
return;
}
const startDateFormatted = format(value.from, "yyyy-MM-dd");
const endDateFormatted = value.to
? format(value.to, "yyyy-MM-dd")
: startDateFormatted;
form.setValue("dateRange", {
startDate: startDateFormatted,
endDate: endDateFormatted,
});
},
[form]
);
I am sure there are better ways to implement this. But this is the way I implemented it. I hope it helps you.