Search code examples
javascriptinputdelaysmoothingsearch-suggestion

Input box in react to show suggestions in reactjs


How to make an input box which will give suggestion with small delay not on every character input fast. I don't want to hit suggestion api on every char input.

class Input extends React.Component {
constructor (props){
super(props);
this.state = {
  inputVal:'',
  suggestion : []
}
}

handleChange = ({target:{value}})=>{
  fetch('https://api.github.com/search/users?q='+value, (res)=>{
  this.setState({suggestions:res.items});
  });  
}


  render(){
    <input onChange={this.handleChange} value {this.state.inputVal} />
    <ul id="suggestions">
    this.state.suggestions.map(sugg=>{
      return (
      <li>{sugg.login}</li>
      )
    })
    </ul>
  }
}

ReactDOM.render(<Input />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='container'></div>


Solution

  • You can use delayed API call using setTimeout that is cleared after each change of input value. Here's a small working example:

    const INPUT_TIMEOUT = 250; //ms - It's our input delay
    class TodoApp extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            value: '',
            predictions: [],
          };
    
          this.onChange = this.onChange.bind(this);
        }
    
        getPredictions(value) {
          // let's say that it's an API call
          return [
            'Boston',
            'Los Angeles',
            'San Diego',
            'San Franciso',
            'Sacramento',
            'New York',
            'New Jersie',
            'Chicago',
          ].filter(item => item.toLowerCase().indexOf(value.toLowerCase()) !== -1);
        }
    
        onChange(e) {
          // clear timeout when input changes value
          clearTimeout(this.timeout);
          const value = e.target.value;
          this.setState({
            value
          });
    
          if (value.length > 0) {
            // make delayed api call
            this.timeout = setTimeout(() => {
              const predictions = this.getPredictions(value);
              this.setState({
                predictions
              });
            }, INPUT_TIMEOUT);
          } else {
            this.setState({
              predictions: []
            });
          }
        }
    
        render() {
            return ( 
              <div >
              <input type = "text" value={this.state.value} onChange = {this.onChange}/>
                <div> 
                {
                  this.state.predictions.map((item, index) => (
                    <div key={index + item}>{item}</div>
                  ))
                } 
                </div> 
              </div>
            )
        }
    }
    
    ReactDOM.render( <TodoApp />, document.querySelector("#app"))
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    
    <div id="app"></div>