olahol / react-tagsinput

Highly customizable React component for inputing tags.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

image

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!