EdG
EdG

Reputation: 2351

Fetch suggestions dynamically from an api for input field

I am trying to fetch the suggestion for input field in my react component. I trigger onChange event on this input field which then makes an api call and fetched the relevant content.

This is how I am doing it

<span>
    <input type="text" placeholder="Add tag" list="languages" id='tags-input-field'
           className={`addtag-input ${this.state.showAddTagInputArray.findIndex(x => x === item.id) !== -1 ? "" : "hidden"}`}
           onChange={this.searchTags.bind(this)}
           />
    <datalist id='languages'>
        {this.state.existingTags && this.state.existingTags.map((item,i)=>{
            return <option value= {item.name} />
        })}
    </datalist>
</span>

And this is "searchTags" function

searchTags(){

            let req = fetch(url + document.getElementById("tags-input-field").value,
                {
                    method: "GET",
                    headers: {
                        "Authorization": "Token " + this.props.token_reducer.token,
                        "content-type": "application/json"
                    }
                })
        req.then(response => response.json()
        ).then(response => {
            console.log(response)
            this.setState({existingTags:response})
        })
    }

Problem: Problem with this approach is, searchTags() gets fired for each letter I press. Suppose I type 10 letters very fast then 10 requests starts and this is hanging my system. How should I approach this problem?

Upvotes: 0

Views: 1141

Answers (2)

kuiro5
kuiro5

Reputation: 1601

This is a very common problem with search inputs. The simplest solution that covers the common use cases is to debounce the call.

constructor () {
 this.searchTags.bind(this);
 this.debouncedSearch = this.debounce(this.searchTags, 300);
}
debounce = (func, wait = 100) => {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, args);
    }, wait);
  };
};

// in render
onChange={this.debouncedSearch}

Lodash also has a great off-the-shelf debounce.

You also need to add a key to each list item since you are rendering a list. Don't use index as your key, choose a unique identifier.

<datalist id='languages'>
  {this.state.existingTags && this.state.existingTags.map((item,i)=>{
    return <option key={item.id} value= {item.name} />
  })}
</datalist>

Upvotes: 0

dave
dave

Reputation: 64687

You can delay the ajax request using something like setTimeout(fetch, n), and clear the timeout on each key press. This causes it to only search after they have stopped typing for at least n milliseconds.

this.state = {
    searchTimeout: false
}

searchTags(){
    clearTimeout(this.state.searchTimeout);
    let searchTimeout = setTimeout(function() {
        let req = fetch(url + document.getElementById("tags-input-field").value,
                {
                    method: "GET",
                    headers: {
                        "Authorization": "Token " + this.props.token_reducer.token,
                        "content-type": "application/json"
                    }
                })
        req.then(response => response.json()
        ).then(response => {
            console.log(response)
            this.setState({existingTags:response})
        })
    }.bind(this), 500);
    this.setState({searchTimeout});
}

Upvotes: 1

Related Questions