Using Lodash debounce in React to prevent requesting data as long as the user is typing

17,989

Well this is easy with lodash _.debounce.
You wrap your method with it and pass the milliseconds you want to wait.
As for the minimum length of the input, just invoke the new method only if the length is above 2.

Here is a small running example:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: '',
      inputValue: ''
    };

    this.updateMessage = _.debounce(this.updateMessage, 2000);
  }


  onChange = ({ target: { value } }) => {
    this.setState({ inputValue: value });
    if (value.length > 2) {
      this.updateMessage(value);
    }
  }


  updateMessage = message => this.setState({ message });

  render() {
    const { message, inputValue } = this.state;
    return (
      <div>
        <input placeholder="type something..." value={inputValue} onChange={this.onChange} />
        <hr/>
        <div>server call >> wait 2 seconds & min length of 2</div>
        <p>{message}</p>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<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>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.compat.js"></script>
<div id="root"></div>
Share:
17,989

Related videos on Youtube

MountainConqueror
Author by

MountainConqueror

Updated on July 21, 2022

Comments

  • MountainConqueror
    MountainConqueror almost 2 years

    I don't want to fire requests as long as the user is typing. My code should throttle requests so that when the user types quickly, it will fire one request with the latest input value instead of many.

    For now when I'm typing "test" it fires 4 different requests:

    1. "t"
    2. "te"
    3. "tes"
    4. "test"

    So I found lodash _.debounce and _.throttle ( [https://lodash.com/docs/4.17.4#debounce] ) but don't really understand how I can inplement it to my code. Can anyone help me?

    My code:

    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    import './style.css';
    
    import { search } from '../../actions/';
    
    class SearchBar extends Component {
      constructor(props) {
        super(props);
        this.state = { searchTerm: '' };
      }
    
      startSearch(query) {
        const storedTerms = this.props.searchedTerm;
        let foundDuplicate = false;
    
        if (storedTerms.length === 0 && query) {
          return this.props.search(query);
        }
    
        if (storedTerms.length !== 0 && query) {
          const testDuplicate = storedTerms.map(term => term === query);
          foundDuplicate = testDuplicate.some(element => element);
        }
    
        if (storedTerms.length !== 0 && !query) {
          return false;
        }
    
        if (foundDuplicate) {
          return false;
        }
    
      return this.props.search(query);
    }
    
    handleInputChange(term) {
      this.setState({ searchTerm: term });
      this.startSearch(term);
    }
    
    render() {
      return (
        <div className="Search-bar">
          <input
            value={this.state.searchTerm}
            onChange={event => this.handleInputChange(event.target.value)}
          />
        </div>
      );
    }
    
    
    function mapStateToProps(state) {
      return {
        searchedTerm: state.searchedTerm,
        savedData: state.savedData,
      };
    }
    
    function mapDispatchToProps(dispatch) {
      return bindActionCreators({ search }, dispatch);
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(SearchBar);
    

    EDIT:

    Thx to Sagiv b.g, I'm adding some explanation:

    ok, so the user should type more than 2 letters && also my app should wait minimum 2 seconds before starting ajax request


    EDIT2: Thx to Sagiv b.g, for great solution!

    I've changed my code like so:

    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    import _ from 'lodash';
    import './style.css';
    
    import { search } from '../../actions/';
    
    class SearchBar extends Component {
      constructor(props) {
        super(props);
        this.state = { inputValue: '' };
    
        this.startSearch = _.debounce(this.startSearch, 2000);
      }
    
      startSearch(query) {
        const storedTerms = this.props.searchedTerm;
        let foundDuplicate = false;
    
        if (storedTerms.length === 0 && query) {
          return this.props.search(query);
        }
    
        if (storedTerms.length !== 0 && query) {
          const testDuplicate = storedTerms.map(term => term === query);
          foundDuplicate = testDuplicate.some(element => element);
        }
    
        if (storedTerms.length !== 0 && !query) {
          return false;
        }
    
        if (foundDuplicate) {
          return false;
        }
    
        return this.props.search(query);
      }
    
      onChange = ({ target: { value } }) => {
        this.setState({ inputValue: value });
        if (value.length > 2) {
          this.startSearch(value);
        }
      };
    
      render() {
        return (
          <div className="Search-bar">
            <input
              placeholder="Type something to search GitHub"
              value={this.state.inputValue}
              onChange={this.onChange}
            />
          </div>
        );
      }
    }
    
    function mapStateToProps(state) {
      return {
        searchedTerm: state.searchedTerm,
        savedData: state.savedData,
      };
    }
    
    function mapDispatchToProps(dispatch) {
      return bindActionCreators({ search }, dispatch);
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(SearchBar);
    

    Last Bug to deal with

    But it has one last bug, that I don't know how to get rid off. When the user wants to change search query and uses backspace to erase search field, my app always fires unexpectedly another API request. Here is an example:

    https://youtu.be/uPEt0hHDOAI

    Any ideas how I can get rid of that behavior?

  • MountainConqueror
    MountainConqueror over 6 years
    Ok, but I have one last bug to deal with. Should I accept your answer and create new StackOverflow question? Here is my problem -> youtu.be/uPEt0hHDOAI
  • Sagiv b.g
    Sagiv b.g over 6 years
    Seems like its not directly related to this question (seems like a redux issue). So i think a new question is with relevant code example is better