Downshift: Improving docs/examples for simple "dropdown select" component

Created on 23 Apr 2018  路  6Comments  路  Source: downshift-js/downshift

I'm new to Downshift and really impressed with what I've seen so far! I just want to provide some feedback regarding one aspect I'm struggling with - implementing a simple "dropdown select" component (similar to HTML "select" element) where the items are not just strings, but instead "key/value" pairs.

I'm finding that most examples use a string array for their items and also they usually start with an empty default value, allowing the user to type into the input and show suggestions in the dropdown menu. I don't want any input at all. Instead, I want a list of item objects that represent key/value pairs (such as "id" and "label" for example), then I want to be able to set the selectedItem using a string prop that specifies the "item id" only.

When I came to Downshift for the first time, what I was hoping to find was a simple example that demonstrates how to implement the following component in Downshift:

const items = [{ id: 'one', label: 'Item 1' }, { id: 'two', label: 'Item 2' }];
<Select selectedItem='two' items={items}/> 

The requirements being:

  1. Item 2 is selected by default.
  2. items is an array of objects rather than strings.
  3. The selectedItem prop is a string representing the select item as the "id" attribute in the items array.

I've been trying to implement this off and on for a few days now but all my attempts so far feel a bit to hacky for my liking and I think I'm missing something fundamental about how Downshift works. I can鈥檛 seem to figure out the right way to have a selectedItem representing the "id" in the items object, if that makes sense?

Most helpful comment

I usually solve this by doing something like this: https://codesandbox.io/s/xpwovvx5vz

The usage looks like this. The defaultValue or value have to match the value of one of the items in the options array.

<Select
  label="Select fruit"
  defaultValue="apple"
  options={[
    { value: "apple", label: "Apple" },
    { value: "orange", label: "Orange" }
  ]}
/>

When you need to use this in a form I usually use a native <select /> element and sync the selectedItem to it. I can provide an example of it if anyone is interested.

All 6 comments

Hello @pdrummond, I was struggling with the same issue and I came up with a simple solution.

In my case, I've had a <input type="text" /> where while I'm typing, I open a dropdown with the options. My problem was the same as yours, I have a list of objects that I want to bind the value of the input as e.g the id of the object, but I want to display the name of the object for the user in the text field.

I ended up storing the value that I want in a hidden input, and the "fancy" input I display the user friendly label. Check this out:

// ...Downshift wraps this component
<SelectWrapper {...getRootProps({ refKey: 'innerRef' })}>
{/* In the input hidden I store the actual value */}
<input type="hidden" name={name} value={inputValue} /> 

{/* This is the input the user's will se and interact with */}
 <StyledTextInput
    {...getInputProps({ placeholder })}
    label={label}
    isOpen={isOpen}
    onClick={toggleMenu}
    error={error}
    value={selectedItem && selectedItem.label}
  />
  {isOpen ? (
    <OptionList style={{ border: '1px solid #ccc' }}>
      {Children.map(this.props.children, (el, index) =>
        // ... rest of implementation, not relevant

The usage looks like this:

<Select label="Select Industry" name="industries">
  <Select.Option label={'Agriculture'} value={'agriculture'}>
    Agriculture
  </Select.Option>
  <Select.Option label={'Art & Design'} value={'art_design'}>
    Art & Design
  </Select.Option>
</Select>

So in the end, when I am using this component in a form, the value will be stored in the hidden input that I also assigned the name of the input.

Hope this works for you!

I'm also using a hidden input but it really seems to me that it is a code smell. Moreover, while integrating with redux-form and material-ui, I'm facing bugs such as onBlur not working. An example would definitely be appreciated :-)

I usually solve this by doing something like this: https://codesandbox.io/s/xpwovvx5vz

The usage looks like this. The defaultValue or value have to match the value of one of the items in the options array.

<Select
  label="Select fruit"
  defaultValue="apple"
  options={[
    { value: "apple", label: "Apple" },
    { value: "orange", label: "Orange" }
  ]}
/>

When you need to use this in a form I usually use a native <select /> element and sync the selectedItem to it. I can provide an example of it if anyone is interested.

+1

Closing this due to inactivity. Please open a new issue if needed.

Are the work around's suggested here still the best way to achieve this? Isn't this a pretty common use case to need to present the label to the user and then use the value only when submitting the form?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lukyth picture lukyth  路  3Comments

kohgpat picture kohgpat  路  3Comments

mayk93 picture mayk93  路  3Comments

klapouchy picture klapouchy  路  4Comments

gsimone picture gsimone  路  3Comments