10

I have a search input, to make API calls on the fly. I'd like to implement debounce to reduce the amount of server calls.

  _debouncedSearch() {
    debounce(this.props.fetchRoutes(this.state.searchText), 1000);
  }

  _updateResults(searchText) {
    this.setState({searchText});
    this._debouncedSearch();
  }

I am expecting debouncedSearch every 1 second. But it is still called on the fly. And throw errors:

Uncaught TypeError: Expected a function at debounce (lodash.js?3387:10334)

Uncaught Error: A cross-origin error was thrown. React doesn't have access to the actual error object in development.

I feel like this question must get asked around a lot, but none of the solution seems to work for me. Could someone explain to me what exactly is the problem here? I thought debounce is just a setTimeOut.

Thanks

leogoesger
  • 3,476
  • 5
  • 33
  • 71
  • debounce expects a function as an argument, your `this.props.fetchRoutes(this.state.searchText)` probably returns void or something that is not a function, try to debounce a function instead. Using either `() =>this.props.fetchRoutes(this.state.searchText)` or `function() { this.props.fetchRoutes(this.state.searchText) }` P.S. : debounce is not just a setTimeout – skornous Dec 01 '17 at 18:44
  • Just a note for others, if you define a debounce function in your render method, it will recreate on every render, making the debounce ineffective. Wrap it in useMemo. https://dmitripavlutin.com/react-throttle-debounce/ – tim-phillips Jun 21 '21 at 17:48

3 Answers3

10
constructor(props) {
    super(props);
    this.state = {
      searchText: '',
    };
    this._debouncedSearch = debounce(
      () => this.props.fetchRoutes(this.state.searchText),
      1000
    );
  }

  _updateResults(searchText) {
    this.setState({searchText});
    this._debouncedSearch();
  }

Here is the fullworking code in case someone needs it!

leogoesger
  • 3,476
  • 5
  • 33
  • 71
4

_.debounce is already a carried out function (function returns function ) . Then _debouncedSearch should be an attribute of the class , and not method :

  _debouncedSearch=  debounce(() => this.props.fetchRoutes(this.state.searchText), 1000);

instead of :

  _debouncedSearch() {
    debounce(this.props.fetchRoutes(this.state.searchText), 1000);
  }

Also, notice , the first argument of _.debounce is a function (() => this.props.fetchRoutes...) , not directly this.props.fetchRoutes...

Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
  • Not sure if this is related. Is this some loader issue? ./src/components/home/Layout.js Module build failed: SyntaxError: Missing class properties transform. 29 | } 30 | > 31 | _debouncedSearch = debounce( | ^ 32 | () => this.props.fetchRoutes(this.state.searchText), 33 | 1000 34 | ); – leogoesger Dec 01 '17 at 18:55
  • BTW, my syntax is right if you are using the babel plugin : `class properties transform` . However , you are not using it that's why you got `Missing class properties transform....` I recommend to configure your babel to add that plugin and get rid off `constructor` – Abdennour TOUMI Dec 01 '17 at 19:25
1

Recently found this issue helpful. Here's my optimized solution:

const [searchTerm, setSearchTerm] = useState("");
const [result, setResult] = useState([]);

async function fetchData(searchTerm: string) {
  const { data } = await client.query(searchTerm);
  setResult(data);
}

const debounceLoadData = useMemo(() => debounce(fetchData, 700), []);

useEffect(() => {
  window.addEventListener("keydown", debounceLoadData(searchTerm));
  return () => {
    window.removeEventListener("keydown", debounceLoadData(searchTerm));
  };
}, [searchTerm]);

This will only call the fetchData() function once after the user stops typing for 700ms. Then I use useMemo to cache every result. If the user searches the same value multiple times it will return the cached value and not invoke the fetchData() function more than necessary.

Ryan
  • 1,482
  • 2
  • 11
  • 19