clearInput fails on keypress when used with react-autosuggest
skosch opened this issue · comments
Hi!
First off, thank you for the wonderful gift that is this component – truly one of the most satisfying React packages to work with! :)
I'm using react-autosuggest as my input component. If I click on a suggestion, the tag is added and the input is cleared. If, however, I hit Enter on a suggestion, the tag is added and the input is not cleared.
The only way I've been able to clear the input is by typing
onSuggestionSelected={(e, {suggestion}) => { // gets called both on click and keypress
this.refs.tags.addTag(suggestion[0]);
setTimeout(() => this.refs.tags.clearInput(), 100); // <----- ಠ_ಠ
}}
My input is controlled via
currentValue={this.state.currentSearch}
which I can mess with – via setState
– to my heart's content, and watch the value change, until I've hit enter on a suggestion – then suddenly setState
doesn't do anything anymore.
I suspect there is a weird interplay of states/input values at work between this component and react-autosuggest, and _clearInput
(which I can see getting called in the debugger) is racing some other function such that state.tags
ends up not actually getting cleared after Enter is hit.
This may well be an issue in react-autosuggest. If so, I'd be grateful if you could help figure out what behaviour tagsinput is expecting so I can open an issue there. Thanks again!
Did you use their example? I believe you might be looking for this snippet (taken from the example):
...
const handleOnChange = (e, {method}) => {
if (method === 'enter')
e.preventDefault();
else
props.onChange(e);
};
return (
<Autosuggest
inputProps={{...other, onChange: handleOnChange}}
...
/>
);
Thanks! That must've changed with a recent version, because no, I didn't have that.
Sadly though it makes no difference. I can see it entering the enter
branch of your handleOnChange
function, but the input still isn't cleared.
That's surprising. I used their example on my first attempt verbatim and the input was clearing on enter. Would you be able provide the entire class you're trying to implement? Hopefully it's something minor missing :)
Sure, here you go (work in progress, don't judge)
handleChange = (tags) => {
this.props.setValue(this.props.field.path, tags);
}
updateCurrentSearch = (e) => {
const newSearchValue = e.currentTarget.value;
this.setState({
currentSearch: newSearchValue
});
}
render() {
let nameMap = this.props.field.nameMaps[this.props.type];
let nameMapCopy = Object.assign({}, nameMap);
for (let i = 0, l = this.props.value.length; i < l; i++) {
delete nameMapCopy[this.props.value[i]];
}
const availableTags = Object.entries(nameMapCopy);
const autocompleteRenderInput = (props) => {
const inputValue = (props.value && props.value.trim().toLowerCase()) || "";
let suggestions = availableTags.map((idNamePair) => {
return [...idNamePair, idNamePair[1].name.toLowerCase().indexOf(inputValue)];
}).sort((a, b) => a[2] - b[2]).filter(idNameIndexTriplet => idNameIndexTriplet[2] > -1);
const handleAutosuggestChange = (e, {method}) => {
if (method === 'enter') {
e.preventDefault();
} else {
props.onChange(e);
}
}
return (
<Autosuggest
ref={props.ref}
suggestions={suggestions}
shouldRenderSuggestions={(value) => value && value.trim().length > 0}
getSuggestionValue={(suggestion) => suggestion[0]}
renderSuggestion={(suggestion) => <span><span className="suggestion-type">{suggestion[1].type}</span> {suggestion[1].name} <span className="suggestion-email">{suggestion[1].email}</span></span>}
inputProps={{...props, onChange: handleAutosuggestChange}}
onSuggestionSelected={(e, {suggestion}) => {
this.refs.tags.addTag(suggestion[0]);
}}
/>
)
};
and further down:
<TagsInput
ref="tags"
renderInput={autocompleteRenderInput}
validationRegex={validationRegex}
renderTag={renderTagFunc}
value={this.props.value}
onlyUnique
onChange={this.handleChange}
currentValue={this.state.currentSearch}
inputProps={{onChange: this.updateCurrentSearch, placeholder: 'Type to add a recipient ...', style: {minWidth: '20em'}}}
/>
I used the following as my tags input:
<TagsInput inputProps={{placeholder: 'Enter tags to filter views'}} renderInput={autocompleteRenderInput} value={this.state.tags} onChange={::this.handleChange} />
And changed around your example a bit to work with a dataset I have:
const autocompleteRenderInput = (props) => {
const {addTag, ...other} = props;
const inputValue = (props.value && props.value.trim().toLowerCase()) || '';
const inputLength = inputValue.length;
const {options} = this.state;
const suggestions = options.filter((state) => {
return state.name.toLowerCase().slice(0, inputLength) === inputValue
});
const handleAutosuggestChange = (e, {method}) => {
if (method === 'enter') {
e.preventDefault();
} else {
props.onChange(e);
}
}
return (
<Autosuggest
ref={props.ref}
suggestions={suggestions}
shouldRenderSuggestions={(value) => value && value.trim().length > 0}
getSuggestionValue={(suggestion) => suggestion.name}
renderSuggestion={(suggestion) => <span>{suggestion.name}</span>}
inputProps={{...other, onChange: handleAutosuggestChange}}
onSuggestionSelected={(e, {suggestion}) => {
addTag(suggestion.name);
}}
/>
)
}
The above does clear input on enter. If you would like to see specifically what I changed:
Wow, that's some first class handholding right there, with colour-coded diffs and everything 😃 thanks so much for your help, I really appreciate it!
Turns out using the addTag
function from the props
(instead of this.refs.tags.addTag
, which I'd gotten from an older example) in combination with the handleAutosuggestChange
made all the difference. Thanks again!