downshift version: v3.3.5node version: v11.11.0npm (or yarn) version: 1.13.0Relevant 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:
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.mouseUpDownshift.stateChangeTypes.clickItemThough 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.