My Grocery Book

Simple Reactjs AutoComplete using Typehead’s Bloodhound

I wanted to add autocomplete to the shopping list, hopefully making it easier to build them. Using Reactjs made it very difficult to use out of the box autocompletes like typeahead and jquery ui autocomplete because Reactjs uses a virtual dom. Another requirement I wanted was to display the suggestions above the input making it a bit more mobile friendly.

The solution I came up with is using typeahead’s bloodhound engine and adding the results to the reactjs render myself.

Here is an example of the minimum code required to get this running:

var SampleAutoComplete = React.createClass({

  getInitialState: function () {
    return {name: "", suggestions: [], 
            more_suggestions: [], bloodhound_initialized: false};
  },

  handleNameChange: function (e) {
    if(e.target.value === "") {
      this.setState({name: "", suggestions: [], more_suggestions: []});
    } else {
      this.findSuggestions(e.target.value);
      this.setState({name: e.target.value});
    }
  },

  findSuggestions: function(name) {
    if (this.state.bloodhound_initialized) {
      this.bloodhound.search(name,
          this.handleNameSuggestions,
          this.handleAdditionalNameSuggestions)
    }
  },

  handleNameSuggestions: function (suggested_names) {
    this.setState({suggestions: suggested_names});
  },

  handleAdditionalNameSuggestions: function (suggested_names) {
    this.setState({more_suggestions: suggested_names});
  },

  componentDidMount: function () {
    this.bloodhound = Bloodhound({
      datumTokenizer: Bloodhound.tokenizers.whitespace,
      queryTokenizer: Bloodhound.tokenizers.whitespace,
      sufficient: 3,
      prefetch: { url: "/my/prefetch/url.json" },
      remote: { url: "/my/remote/url.json?query=%QUERY", 
                wildcard: '%QUERY' }
    });
    this.bloodhound.initialize().done(this.bloodhoundInitialized);
  },

  bloodhoundInitialized: function () {
    this.setState({bloodhound_initialized: true});
  },

  render: function () {
    var state = this.state;

    var suggestions = state.suggestions.concat(this.state.more_suggestions).slice(0,3);

    var rendered_suggestions = suggestions.map(function (name) {
      return (
          <button className="name-suggestion" key={"suggested-" + name}>{name}</button>
      );
    });

    return <div>
      <div className="col-xs-12">{rendered_suggestions}</div>
      <div className="col-xs-12">
        <input value={state.name} onChange={this.handleNameChange}
               autoComplete={state.bloodhound_initialized ? 'off' : 'on'} />
      </div>
    </div>;
  }
});

You can find the grocery app version of the code in theses pull requests: Shopping list autocomplete and Additional suggestions on shopping list

Also feel free to login as a Guest and give it a try yourself with creating a shopping list on My Grocery Price Book

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s