Downshift: selectedItem remains the same when input blur with empty value

Created on 13 Nov 2019  路  6Comments  路  Source: downshift-js/downshift

  • downshift version: v3.3.5
  • node version: v11.11.0
  • npm (or yarn) version: 1.13.0

Relevant code or config

const Autocomplete = ({
  suggestions,
  selectedItem,
  itemToString,
  onInputValueChange,
  onChange,
  // ======MUITextFieldProps=====
  InputProps,
  ...MUITextFieldProps
}) => {
  const classes = useStyles();

  return (
    <Downshift
      selectedItem={selectedItem}
      itemToString={itemToString}
      onInputValueChange={onInputValueChange}
      onChange={onChange}
    >
      {({
        getInputProps, getItemProps, getMenuProps, highlightedIndex, isOpen,
      }) => (
        <div className={classes.root}>
          <TextField
            fullWidth
            type="text"
            margin="normal"
            InputProps={getInputProps({
              ...InputProps,
            })}
            {...MUITextFieldProps}
          />
          <div {...getMenuProps()}>
            {isOpen && (
            <Paper className={classes.paper}>
              {suggestions.map((suggestion, index) => {
                const itemProps = getItemProps({
                  item: suggestion,
                });
                const selected = highlightedIndex === index;

                return (
                  <ListItem
                    {...itemProps}
                    key={suggestion.key}
                    selected={selected}
                    component="div"
                  >
                    {suggestion.label}
                  </ListItem>
                );
              })}
            </Paper>
            )}
          </div>
        </div>
      )}
    </Downshift>
  );
};

What you did:

1) I selected an item
2) Click outside an input
3) Remove all symbols from input
4) Click outside an input

What happened:

SelectedItem remains the same. But I expect SelectedItem have to null.

Reproduction repository:

https://codesandbox.io/s/github/kentcdodds/downshift-examples/tree/master/?module=%2Fsrc%2Fordered-examples%2F02-complete-autocomplete.js&moduleview=1

question

All 6 comments

You can fix this issue by setting the selectedItem as null when inputValue becomes from something with content to empty string. Maybe in stateReducer.

I will take this into consideration while developing useAutocomplete. If you want you can create a PR that fixes this use case for Downshift. Thanks for reporting it!

I think that when I clear input and click outside it, it should trigger the event Downshift.stateChangeTypes.blurInput, so that I handle this event

switch (changes.type) {
  case Downshift.stateChangeTypes.blurInput:
    console.log('blurInput')
    return changes
  default:
    return changes
  }
}

but when inputValue !== "" and selectedItem !== null Downshift.stateChangeTypes.blurInput is not raised. How to handle this situation?

Hmm why not? Do you have a repo with this? Or maybe point where this happens in the downshift.js file? Thanks for looking into this!

@perevezencev If you want to clear the selection, I would suggest adding a button (or some other CTA) next to the input that would explicitly clear the selection using the clearSelection helper.

I think that when I clear input and click outside it, it should trigger the event Downshift.stateChangeTypes.blurInput, so that I handle this event

I think you might be trying to deal with the situation in the wrong place. According to the docs for the state reducer:

NOTE: This is only called when state actually changes. You should not attempt to use this to handle events. If you wish to handle events, put your event handlers directly on the elements (make sure to use the prop getters though! For example: <input onBlur={handleBlur} /> should be <input {...getInputProps({onBlur: handleBlur})} />).

It would be nice if you could just use the onOuterClick prop to handle this, but I just tested and the inputValue is already reset to the selectedItem's original itemToString value. Bummer.

You are passing selectedItem and onChange through to the downshift component from whatever component is rendering your Autocomplete, so controlling those props may paint you in a corner and limit your options. This means you need that outer context to change selectedItem based on certain conditions whenever onChange is called, which, according to the docs, is:

Called when the selected item changes, either by the user selecting an item or the user clearing the selection.

I believe "clearing the selection" happens when calling the function I referred to earlier, clearSelection(). So onChange may not be the only thing you want to expose as a pass-through prop if you are dead-set on controlling those props.

One of your options is to do your thing in the input's onBlur handler while also not controlling the selectedItem prop (maybe initialSelectedItem is what you really want anyway?). Then you could do something like:

// note that I added `clearSelection` and `inputValue` here
{({
  getInputProps, getItemProps, getMenuProps, highlightedIndex, isOpen, clearSelection, inputValue
}) => (
  <div className={classes.root}>
    <TextField
      fullWidth
      type="text"
      margin="normal"
      InputProps={getInputProps({
        ...InputProps,
        onBlur: (evt) => {
          !inputValue && clearSelection();
        }
      })}
      {...MUITextFieldProps}
    />
    // ...

I'm still working through all the different combinations of props to determine what works best with my own use case, but I've been experimenting with downshift for awhile now and I'm starting to feel like I'm getting the hang of it, so don't give up! 馃構

@silviuavram FWIW, with regard to the OP, I actually prefer this to be the default functionality. To me, simply deleting the input content does not indicate a selection, so triggering an outerClick or pressing Esc should preserve the currently selectedItem.

I _do_, however, think it's a bit confusing to include event names in the stateChangeTypes and then tell people _not_ to use those types to handle events. It also seems like there are cases where multiple event types _should_ be called but aren't because the state change only pops up once. For example, When clicking an item in the menu with my mouse, I would expect at least these change types to occur:

  • Downshift.stateChangeTypes.mouseUp
  • Downshift.stateChangeTypes.clickItem

Though I'm pretty sure the single click triggers a single state change, so only one call to the state reducer with one change type will come through. This is a pretty tough problem to tackle, but if I think of any ideas, I'll let ya know! 馃榿

After the occurrence of this problem, I was forced to switch to material-ui/lab Autocomplete

Thanks @Smolations

onBlur: (evt) => { !inputValue && clearSelection(); }

combined with

onChange={selection => { onSelect(selection ? selection.value : ""); }}

Worked.

Was this page helpful?
0 / 5 - 0 ratings