Select component inside a form acts indeterminately
Bug report
Current Behavior
If a controlled <Select /> component is used inside a form, after changing the value passed to it, the onValueChange method will get called with an empty string value ('' ).
https://github.com/user-attachments/assets/30c48587-782d-4de8-ae12-042d8be7ab95
in the video above, I'm using the Shadcn/ui select component, note that the component is inside a form and I have a console.log statement inside the onChange passed to the onValueChanged prop. on the initial render, the controlled value is undefined, after setting this value the onChange function is called with a value of an empty string.
Expected behavior
I believe that the onChange method should only be called when a user interaction changes the value of Select and it's like so when the Select component is not used inside a form.
Reproducible example
https://stackblitz.com/edit/vitejs-vite-gua7y3?file=src%2FApp.tsx
Suggested solution
I guess this behavior is somewhat related to supporting browser auto-complete and I don't really know now how to fix this without interfering with that but I probably would prevent this onChange from happening if the Select component is being used as a controlled input.
Your environment
| Software | Name(s) | Version |
|---|---|---|
| Radix Package(s) | Select | 2.1.1 |
| React | n/a | 18 |
| Browser | chrome | 128 |
| Assistive tech | ||
| Node | n/a | |
| npm/yarn | ||
| Operating System |
same here, any solution? the problem happens only when i use Select inside a form, if i use a simple div instead it works perfectyl
any solution?
what I did in my onChange function was to check if options are not loaded or the value passed to it was an empty string I would return early. not the best solution but works fine.
Same.
Using this workaround to avoid unexpected onValueChange triggers after form.reset from RHF.
NOTE: If your rely on "" to show a placeholder, this won't work of course.
This is a big problem. We could at least get some kind of event source or attribute to decide if we can ignore the value change. But with lack of context, the proposed workarounds have to sacrifice a potentially valid value as a clue that the even bubbled up from a non-user interaction source.
The issue with sacrificing empty string is that it could be used as a way to clear the value via a clear icon button.
I have an example of a form that saves fields individually on value change. So not the typical form submit. In this setup, the select fields trigger onValueChange() which in turn submits data to the backend as soon as they're loaded.
Ran into this bug and spent like 6 hours, I was like what's happening, didn't expect that <Select> would act on its own to trigger onValueChange .
Yup running into the same issue here. Fighting an uphill battle to implement react-hook-form with defaultValues hydrated from a persistent zustand store (with ReactRouter 7/Remix). Both form.setValue and form.reset successfully hydrate the form state but both cause the Shadcn (thus radix-ui) Select to call the onValueChange with an empty string "".
Searched around for hours and tried just about every suggestion. Going to stick with the same thing as @MiloWang2048 as I can live with not setting actual empty strings on my inputs. Hope this helps push the SEO for this issue a little bit.. It was deeply buried.
This is happening to me as well. I have one NextJS page with navigation. It looks like remounting the component will trigger the event.
https://github.com/user-attachments/assets/db6e7289-e321-4f0e-9607-6a54e103d432
Here is my workaround, which seems to do its job:
onValueChange={(e) => {
// WORKAROUND FOR: https://github.com/radix-ui/primitives/issues/3135
const stack = new Error().stack;
if (stack?.includes("SelectBubbleInput")) {
return;
}
onValueChange(e);
}}
It seems that setting a key based on the value to force the component to remount fixes this
<Select
onValueChange={field.onChange}
value={field.value ?? ""}
key={field.value ? `status-${field.value}` : "status-initial"}
>
<SelectTrigger>
<SelectValue placeholder="Select status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="draft">Draft</SelectItem>
<SelectItem value="published">Published</SelectItem>
</SelectContent>
</Select>
Same thing happening here. Wasted an hour on this...
Spent also like 3 hours on this issue will apply @mdobro solution but would like this to get fixed!
Same issue here. At the very least, if onValueChange gets triggered when a controlled value changes, the value should be the same, not just an empty string...
Same issue, 2 hours here.
Running into the same exact issue!
It seems that setting a key based on the value to force the component to remount fixes this
<Select onValueChange={field.onChange} value={field.value ?? ""} key={field.value ? `status-${field.value}` : "status-initial"} > <SelectTrigger> <SelectValue placeholder="Select status" /> </SelectTrigger> <SelectContent> <SelectItem value="draft">Draft</SelectItem> <SelectItem value="published">Published</SelectItem> </SelectContent> </Select>
Yes exactly, i was also encountering a similar issue as OP, as mentioned by you, setting the key prop fixes the issue.