react-autosuggest icon indicating copy to clipboard operation
react-autosuggest copied to clipboard

Input value lost when method === 'click'

Open bildungsroman opened this issue 6 years ago • 2 comments

Are you reporting a bug? Yes

  • Please create a Codepen that demonstrates your issue. I'm not able to create a codepen, as this code is running in our own wrapper component in a private repo. I'll do my best to reproduce the relevant code here.

I'm hitting an issue specifically when the selection method is 'click' (rather than 'down' and 'tab' or 'enter' or manually filling out the value): the value is not saved in state, and shows as null. I know this is an issue specifically with my autosuggest component, as manually filling in the value works as expected and is saved in state.

  • Provide the steps to reproduce the issue, e.g.:

    1. Focus on the input field
    2. Type c, and wait for suggestions to appear
    3. Click on one of the suggestions

    Observed behaviour: The suggestion appears as it should in the field, but its value is not passed to state, and my app throws an error because an expected string is actually null.

    Expected behaviour: The value is passed from the autosuggest field to state no matter how it is entered, whether clicking on the suggestion or hitting down and enter to select the suggestion.

Here is the relevant code from my component:

import React, { Component } from 'react';
import Autosuggest from 'react-autosuggest';

const Suggestion = suggestion => <span>{suggestion}</span>;

class AutoSuggest extends Component {
  constructor (props) {
    super(props);

    this.state = {
      suggestions: props.suggestions,
      value: props.value || ''
    };

    this.getSuggestions = this.getSuggestions.bind(this);
    this.onSuggestionsFetchRequested = this.onSuggestionsFetchRequested.bind(this);
    this.onSuggestionsClearRequested = this.onSuggestionsClearRequested.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  getSuggestions (value) {
    const inputValue = value.trim().toLowerCase();
    const inputLength = inputValue.length;

    return inputLength === 0 ? [] : this.props.suggestions.filter(suggestion =>
      suggestion.toLowerCase().includes(inputValue) && suggestion.toLowerCase() !== inputValue
    );
  }

  // Autosuggest will call this function every time you need to update suggestions.
  onSuggestionsFetchRequested ({ value }) {
    this.setState({
      suggestions: this.getSuggestions(value)
    });
  }

  // Autosuggest will call this function every time you need to clear suggestions.
  onSuggestionsClearRequested () {
    this.setState({
      suggestions: []
    });
  }

  onSuggestionSelected (event, { method }) {
    if (method === 'enter') {
      event.preventDefault();
    }
  }

  onChange (event, { newValue }) {
    /* The Autosuggest component doesn't always have the name attribute set on
     * the event target. We must set the name on the target of the event to
     * ensure the value is saved correctly. */
    event.target.name = this.props.name;
    event.target.value = newValue;

    // this is not working when method === 'click'
    this.setState({ value: newValue });

    this.props.onChange(event);
  }

  render () {
    const {
      suggestions,
      value
    } = this.state;

    const {
      placeholder,
      required,
      name,
      pattern,
      readOnly
    } = this.props;

    return (
      <Autosuggest
        suggestions={suggestions}
        getSuggestionValue={suggestion => suggestion}
        onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
        onSuggestionsClearRequested={this.onSuggestionsClearRequested}
        onSuggestionSelected={this.onSuggestionSelected}
        renderSuggestion={Suggestion}
        inputProps={{ onChange: this.onChange, placeholder, value, required, name, pattern, readOnly }}
      />
    );
  }
}

export default AutoSuggest;

bildungsroman avatar Jan 10 '19 00:01 bildungsroman

Any update? I am facing this issue also.

ghost avatar Sep 23 '19 13:09 ghost

A bit late, but the problem is that newValue is undefined when 'click' method is used.

Just modify the value to set in your onChange function:

this.setState({ value: newValue ?? event.target.firstChild.data });

barbel-thierry avatar Apr 14 '21 09:04 barbel-thierry