I'm implementing a form with react-hook-form and useFieldArray where users can add multiple account sets (cash account + trade account + currency) and set one as default using a toggle switch. However, the Switch component from shadcn/ui isn't responding immediately when clicked. Only one account should be default at any time (radio button behavior). Currently, clicking the Switch doesn't change its visual state immediately.
this is the form:
export function CustodianForm() {
const form = useForm<CustodianFormInput>({
resolver: zodResolver(custodianFormSchema),
defaultValues: initialData || defaultCustodianFormValues,
});
const {
fields: cashFields,
append: appendCash,
remove: removeCash,
} = useFieldArray({
control: form.control,
name: "accounts",
});
useEffect(() => {
const accounts = form.getValues("accounts");
if (accounts.length === 1 && !accounts[0].isdefault) {
form.setValue("accounts.0.isdefault", true);
}
}, [cashFields.length, form]);
const handleAppendAccount = () => {
appendCash({
cashAccountNumber: "",
tradeAccountNumber: "",
currency: "",
isdefault: false,
});
};
const handleSetDefault = (index: number) => {
const currentAccounts = form.getValues("accounts");
const updatedAccounts = currentAccounts.map((account, i) => ({
...account,
isdefault: i === index,
}));
form.setValue("accounts", updatedAccounts);
};
const handleRemoveAccount = (index: number) => {
const isRemovingDefault = form.getValues(`accounts.${index}.isdefault`);
removeCash(index);
if (isRemovingDefault && cashFields.length > 1) {
setTimeout(() => {
form.setValue("accounts.0.isdefault", true);
}, 0);
}
};
const onSubmit = (values: CustodianFormInput) => {
console.log("Submitted values:", values);
form.reset();
onClose();
};
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="font-poppins space-y-[8px] max-h-[80vh] overflow-y-auto scrollbar-thin scrollbar-thumb-zinc-50 pr-4 scrollbar-track-transparent"
>
<div className="grid grid-cols-2 gap-6">
<FormField
control={form.control}
name="custodianArabicName"
render={({ field }) => (
<FormItemWrapper>
<FormLabel>Custodian Arabic Name</FormLabel>
<FormControl>
<Input
placeholder="سوق"
{...field}
className="bg-zinc-50 text-right"
dir="rtl"
/>
</FormControl>
</FormItemWrapper>
)}
/>
<FormField
control={form.control}
name="custodianEnglishName"
render={({ field }) => (
<FormItemWrapper>
<FormLabel>Custodian English Name</FormLabel>
<FormControl>
<Input
placeholder="Enter custodian name"
{...field}
className="bg-zinc-50"
/>
</FormControl>
</FormItemWrapper>
)}
/>
</div>
{cashFields.length > 1 && (
<div className="font-bold font-medium pb-[5px]">
Custodian Accounts
</div>
)}
{cashFields.length > 1 && (
<div className="col-span-3 flex items-center gap-6">
<div className="w-[228px]">
<FormLabel>Cash Account Number</FormLabel>
</div>
<div className="w-[228px]">
<FormLabel>Trade Account Number</FormLabel>
</div>
<div className="w-[223px]">
<FormLabel>Account Currency</FormLabel>
</div>
<div className="w-[82px] text-center">
<FormLabel>Set Default</FormLabel>
</div>
</div>
)}
<div
className={`col-span-3 ${cashFields.length > 1 ? "pb-[20px]" : ""}`}
>
{cashFields.map((field, index) => (
<div key={field.id} className="flex gap-6 items-start">
<FormField
control={form.control}
name={`accounts.${index}.cashAccountNumber`}
render={({ field }) => (
<FormItemWrapper>
{cashFields.length === 1 && (
<FormLabel>Cash Account Number</FormLabel>
)}
<FormControl>
<Input
placeholder="Cash account number"
{...field}
className={`${
cashFields.length === 1 ? "w-[265px]" : "w-[225px]"
} bg-zinc-50`}
/>
</FormControl>
</FormItemWrapper>
)}
/>
<FormField
control={form.control}
name={`accounts.${index}.tradeAccountNumber`}
render={({ field }) => (
<FormItemWrapper>
{cashFields.length === 1 && (
<FormLabel>Trade Account Number</FormLabel>
)}
<FormControl>
<Input
placeholder="Trade account number"
{...field}
className={`${
cashFields.length === 1 ? "w-[265px]" : "w-[225px]"
} bg-zinc-50`}
/>
</FormControl>
</FormItemWrapper>
)}
/>
<FormField
control={form.control}
name={`accounts.${index}.currency`}
render={({ field }) => (
<FormItemWrapper>
{cashFields.length === 1 && (
<FormLabel>Account Currnency</FormLabel>
)}
<FormControl>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<SelectTrigger
className={`${
cashFields.length === 1 ? "w-[265px]" : "w-[225px]"
} bg-zinc-50`}
>
<SelectValue placeholder="Select currency" />
</SelectTrigger>
<SelectContent>
{currencies.map((currency) => (
<SelectItem
key={currency.code}
value={currency.code}
>
{currency.code}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItemWrapper>
)}
/>
{cashFields.length === 1 && (
<span
onClick={handleAppendAccount}
className="text-sm cursor-pointer text-indigo-950 hover:text-black pt-[27px]"
>
+ Add
</span>
)}
{cashFields.length > 1 && (
<div className="flex justify-center w-[82px] h-[36px] pt-[10px]">
<FormField
control={form.control}
name={`accounts.${index}.isdefault`}
render={({ field }) => (
<FormItemWrapper>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={(checked) => {
field.onChange(checked);
if (checked) {
handleSetDefault(index);
}
}}
className="data-[state=checked]:bg-blue-500"
/>
</FormControl>
</FormItemWrapper>
)}
/>
</div>
)}
{cashFields.length > 1 && (
<span
onClick={() => handleRemoveAccount(index)}
className="text-sm cursor-pointer text-red-500 font-semibold hover:text-red-700 pt-[10px]"
>
Remove
</span>
)}
</div>
))}
{cashFields.length > 1 && (
<span
onClick={handleAppendAccount}
className="text-sm cursor-pointer text-indigo-950 hover:text-black"
>
+ Add
</span>
)}
</div>
<div>
<FormField
control={form.control}
name="company"
render={({ field }) => (
<FormItemWrapper>
<FormLabel>Company Profile</FormLabel>
<FormControl>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<SelectTrigger className="w-full max-w-[450px] bg-zinc-50">
<SelectValue placeholder="Select company" />
</SelectTrigger>
<SelectContent>
{companies.map((company) => (
<SelectItem key={company.value} value={company.value}>
{company.label}
</SelectItem>
))}
</SelectContent>
</Select>
</FormControl>
</FormItemWrapper>
)}
/>
</div>
<FileUploader />
<div className="flex justify-end">
<Button type="submit" className="bg-blue-500 hover:bg-blue-700">
Save
</Button>
</div>
</form>
</Form>
);
}
How can I make the Switch component respond immediately to clicks while maintaining the "only one default account" requirement?