primitives icon indicating copy to clipboard operation
primitives copied to clipboard

Select component inside a form acts indeterminately

Open mhsattarian opened this issue 1 year ago • 15 comments

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.

CleanShot 2024-09-23 at 16 55 50

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

mhsattarian avatar Sep 23 '24 13:09 mhsattarian

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

eessadrati avatar Sep 27 '24 16:09 eessadrati

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.

mhsattarian avatar Sep 30 '24 13:09 mhsattarian

Same.

Using this workaround to avoid unexpected onValueChange triggers after form.reset from RHF.

Image

NOTE: If your rely on "" to show a placeholder, this won't work of course.

MiloWang2048 avatar Feb 17 '25 08:02 MiloWang2048

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.

simoami avatar Mar 04 '25 22:03 simoami

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 .

nurbek-mirai avatar Mar 06 '25 09:03 nurbek-mirai

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.

Jasperrr91 avatar Apr 09 '25 22:04 Jasperrr91

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

d4rth-v4d3r avatar Apr 14 '25 19:04 d4rth-v4d3r

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);
}}

zoodirector avatar May 17 '25 21:05 zoodirector

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>

mdobro avatar May 28 '25 16:05 mdobro

Same thing happening here. Wasted an hour on this...

HasanMothaffar avatar May 29 '25 10:05 HasanMothaffar

Spent also like 3 hours on this issue will apply @mdobro solution but would like this to get fixed!

faustinozanetto avatar Jul 16 '25 17:07 faustinozanetto

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...

Victor-Mestre-Sybo avatar Aug 26 '25 13:08 Victor-Mestre-Sybo

Same issue, 2 hours here.

joelclimbsthings avatar Nov 10 '25 19:11 joelclimbsthings

Running into the same exact issue!

kevinlandsberg avatar Nov 14 '25 15:11 kevinlandsberg

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.

Ajay-Malviya avatar Nov 21 '25 17:11 Ajay-Malviya