ui icon indicating copy to clipboard operation
ui copied to clipboard

Default value not showing the correct selected value

Open jungrama opened this issue 2 years ago • 20 comments
trafficstars

I have a button with action to set a sorting on select input. On that button i change the value on useState from latest -> price-low-high.

When the button clicked the value inside render-value class changed. but the default value for the select not changed.

const [sort, setSort] = useState<string>('latest')

<div className="flex items-center w-full md:w-auto">
  <div className="render-value">{sort}</div>
  <button onClick={()=> setSort('price-low-high')}>CHANGE SORT VALUE<button>

  <Label htmlFor="sorting" className="mr-2 text-gray-500 hidden md:block">Sorting {sort}</Label>
  <Select onValueChange={value=> selectSort(value)}
    defaultValue={sort}>
    <SelectTrigger className="w-full md:w-[200px]">
      <SelectValue placeholder="Select Sorting" />
    </SelectTrigger>
    <SelectContent id="sorting">
      <SelectItem value="latest">Latest Product {sort}</SelectItem>
      <SelectItem value={'price-low-high'}>Price: Low - High</SelectItem>
      <SelectItem value={'price-high-low'}>Price: High - Low</SelectItem>
    </SelectContent>
  </Select>
</div>

jungrama avatar Aug 27 '23 11:08 jungrama

I am having a similar issue too. It seems that "dynamically" generated SelectItem is causing an issue. In my case, I am using Zod validation library. I am supposed to select from available types of properties. The selection works and Zod is getting the value. But the item is not being displayed in the select box. It is just showing empty

<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem className="grid grid-cols-4 items-center gap-4">
   <FormLabel className="sm:block sm:text-right">
      Type
   </FormLabel>
   <Select
      disabled={isSubmitting}
      onValueChange={field.onChange}
      defaultValue={field.value}
      >
      <FormControl>
         <SelectTrigger className="col-span-4 sm:col-span-3">
            <SelectValue placeholder="Select a property type"/>
         </SelectTrigger>
      </FormControl>
      <SelectContent className="SelectContent">
         {propTypes.map((type) => (
         <SelectItem key={type.id} value={type.id}>
            {type.name}
         </SelectItem>
         ))}
      </SelectContent>
   </Select>
</FormItem>
)}
/>

image

I am not seeing the value selected:

image

However, Zod is getting the value successfully:

image

Now, if I replace the dynamically generated values with static values as below, it will work, and I will be able to see what I selected:

<SelectContent>
   <SelectItem value="[email protected]">[email protected]</SelectItem>
   <SelectItem value="[email protected]">[email protected]</SelectItem>
   <SelectItem value="[email protected]">[email protected]</SelectItem>
</SelectContent>

Something is wrong when we generate the SelectItem dynamically

yousihy avatar Sep 06 '23 09:09 yousihy

Here is codesandbox view for your reference

codesandbox view

yousihy avatar Sep 06 '23 11:09 yousihy

@yousihy , have you resolved this?

codingwithashu avatar Sep 18 '23 17:09 codingwithashu

@yousihy @codingwithashu I hope you are aware of the fact that defaultValue should be used only when you do not need to control the state of the select. You might need to use value instead. See This

hardikchopra242 avatar Sep 20 '23 11:09 hardikchopra242

I was also having trouble getting this to work. But @hardikchopra242 nailed it for me. The example of usage in a form in the docs leads to this mistake (defaultValue instead of value).

image

lucas-mirror avatar Sep 20 '23 23:09 lucas-mirror

@yousihy , have you resolved this?

For my case, the issue was that I needed to strictly specify that the value is a string although the type was a string. This applies to ALL shadcn controls by the way.

So, I had to convert the string to a string

value={data.id.toString()}

So, the SelectContent would look like this:

<SelectContent>
     {propType.map((data)=>(
      <SelectItem key={data.id} value={data.id.toString()}>{data.name}</SelectItem>
    ))}
</SelectContent>

yousihy avatar Sep 22 '23 05:09 yousihy

@yousihy @codingwithashu I hope you are aware of the fact that defaultValue should be used only when you do not need to control the state of the select. You might need to use value instead. See This

This did not work for me or it didn't have any effect on my problem. My controls are working fine with value only. My issue was that I needed to ensure that I am passing a string to the value property of the SelectItem even though the type of that value is actually a string. See my comment above

yousihy avatar Sep 22 '23 05:09 yousihy

@yousihy Thanks!

data.id.toString()

this saved me to waste much more time on this

Yolosopher2 avatar Sep 22 '23 09:09 Yolosopher2

Not worked image

codingwithashu avatar Sep 22 '23 11:09 codingwithashu

@codingwithashu

Not worked image

First of all, if you are using value, there is no reason to also use defaultValue.

Inside <Select> attributes comment "value" and "onValueChange". And add .toString() inside defaultValue like this: defaultValue={field.value.toString()}

try if it works

Yolosopher2 avatar Sep 22 '23 11:09 Yolosopher2

Not worked image

Can you upload or write your code in a codesandbox so we can look at it? Maybe we can help

yousihy avatar Sep 22 '23 14:09 yousihy

image I set defaultValue is formfield attribut and value at Selectitem, I set value equal defaultValue (I think defaultValue can use value.key but my defaultValue is same value.name ).

MrKeem avatar Sep 30 '23 19:09 MrKeem

@yousihy @codingwithashu I hope you are aware of the fact that defaultValue should be used only when you do not need to control the state of the select. You might need to use value instead. See This

It works 👍

For set the defaulValues for the form use:

const form = useForm<z.infer<typeof FormSchema>>({
    resolver: zodResolver(FormSchema),
    defaultValues: "your values" ,
  }); 

webdiego avatar Oct 07 '23 14:10 webdiego

As the Radix docs state: If you need more flexibility, you can control the component using value/onValueChange props and passing children to SelectValue.

bennobuilder avatar Dec 16 '23 17:12 bennobuilder

solved 💯 ;) 👍 🥇

Actually, due to the fact that shadcn uses radix in itself and when you are importing the select component, it is probably wrongly imported from radix and you just need to import it from the select component related to shadcn. This was the problem with my code and I realized it after a few days

import { SelectItem, SelectValue } from "@radix-ui/react-select"; //wrong import

import {SelectItem, SelectValue } from "@/components/ui/select"; //correct import

mammad-arvin avatar Jan 21 '24 18:01 mammad-arvin

As the Radix docs state: If you need more flexibility, you can control the component using value/onValueChange props and passing children to SelectValue.

This fixed my issue.

amuelrath avatar Feb 06 '24 02:02 amuelrath

Where is the entire code that works?

dbersan avatar May 04 '24 19:05 dbersan

I've found the best way is to set the defaultValue to be the value from your source object, rather than the field.value. When the source object loads the select will re-render correctly to show the original object value. This wasn't the case when using value={field.value ? field.value.toString() : undefined}, it would always be undefined even after the object had loaded and other fields had re-rendered with the object values.

I wonder if it's because of the number -> string conversion?

                <FormField
                  control={userForm.control}
                  name="roleId"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>Role</FormLabel>
                      <Select onValueChange={field.onChange} defaultValue={user.roleId.toString()}>
                        <FormControl>
                          <SelectTrigger>
                            <SelectValue placeholder="Select a Role for this User" />
                          </SelectTrigger>
                        </FormControl>
                        <SelectContent>
                          <SelectItem value="0">Reader</SelectItem>
                          <SelectItem value="1">Standard</SelectItem>
                          <SelectItem value="2">Admin</SelectItem>
                          <SelectItem value="3">Owner</SelectItem>
                        </SelectContent>
                      </Select>
                      <FormDescription>
                        Security level for this user. This will determine what they can do.
                      </FormDescription>
                      <FormMessage />
                    </FormItem>
                  )}
                />

jhelmink avatar May 20 '24 23:05 jhelmink

Hi all,

What if I want the value in a form submission to be of type object and not string?

For example I want to select an item but that item is an object "behind the scenes" and not a simple string.

Is there a possibility to do that?

Thanks.

andrewpap22 avatar Jun 10 '24 12:06 andrewpap22

https://github.com/shadcn-ui/ui/issues/4021#issuecomment-2192170828

WangLarry avatar Jun 26 '24 16:06 WangLarry

Pass what you want as a default as props const ListItems= (value: string) => {}

Select onValueChange={handleChange} defaultValue={value.toString()}

it worked for me. i hope it will work for you

fuadelgadi avatar Jul 12 '24 15:07 fuadelgadi

Is there a solution ?

camillevingere avatar Jul 22 '24 12:07 camillevingere

I did it like this and it worked...

Using nextjs 14 in a form with zod:

const form = useForm<z.infer<typeof UpdateCompanyFormSchema>>({
    resolver: zodResolver(UpdateCompanyFormSchema),
    defaultValues: {
      companyId: companySelected?.id.toString() || "",
    },
  });

  useEffect(() => {
    if (companySelected) {
      form.setValue("companyId", companySelected.id.toString());
    }
  }, [companySelected, form]);
  
  <FormItem>
    <FormLabel>Nome da empresa</FormLabel>
    <Select
      onValueChange={field.onChange}
      defaultValue={field.value || ""}
    >
      <FormControl>
        <SelectTrigger>
          <SelectValue placeholder="Selecione a empresa" />
        </SelectTrigger>
      </FormControl>
      <SelectContent>
        <SelectGroup>
          <SelectLabel>ATIVAS</SelectLabel>
          {companies
            .filter((company) => company.active)
            .map((company) => (
              <SelectItem
                key={company.id}
                value={company.id.toString()}
              >
                {company.name}
              </SelectItem>
            ))}
        </SelectGroup>
        <SelectGroup>
          <SelectLabel>INATIVAS</SelectLabel>
          {companies
            .filter((company) => !company.active)
            .map((company) => (
              <SelectItem
                key={company.id}
                value={company.id.toString()}
                className="text-gray-700"
              >
                {company.name}
              </SelectItem>
            ))}
        </SelectGroup>
      </SelectContent>
    </Select>
    <FormDescription>Nome da empresa</FormDescription>
    <FormMessage />
  </FormItem>
  )}

RDX777 avatar Jul 30 '24 06:07 RDX777