Lodash debounce with React Input

91,866

Solution 1

The debounce function can be passed inline in the JSX or set directly as a class method as seen here:

search: _.debounce(function(e) {
  console.log('Debounced Event:', e);
}, 1000)

Fiddle: https://jsfiddle.net/woodenconsulting/69z2wepo/36453/

If you're using es2015+ you can define your debounce method directly, in your constructor or in a lifecycle method like componentWillMount.

Examples:

class DebounceSamples extends React.Component {
  constructor(props) {
    super(props);

    // Method defined in constructor, alternatively could be in another lifecycle method
    // like componentWillMount
    this.search = _.debounce(e => {
      console.log('Debounced Event:', e);
    }, 1000);
  }

  // Define the method directly in your class
  search = _.debounce((e) => {
    console.log('Debounced Event:', e);
  }, 1000)
}

Solution 2

With a functional react component try using useCallback. useCallback memoizes your debounce function so it doesn't get recreated again and again when the component rerenders. Without useCallback the debounce function will not sync with the next key stroke.

`

import {useCallback} from 'react';
import _debounce from 'lodash/debounce';
import axios from 'axios';

function Input() {
    const [value, setValue] = useState('');

    const debounceFn = useCallback(_debounce(handleDebounceFn, 1000), []);

    function handleDebounceFn(inputValue) {
        axios.post('/endpoint', {
          value: inputValue,
        }).then((res) => {
          console.log(res.data);
        });
    }


    function handleChange (event) {
        setValue(event.target.value);
        debounceFn(event.target.value);
    };

    return <input value={value} onChange={handleChange} />
}

`

Solution 3

This is how I had to do it after googling the whole day.

const MyComponent = (props) => {
  const [reload, setReload] = useState(false);

  useEffect(() => {
    if(reload) { /* Call API here */ }
  }, [reload]);

  const callApi = () => { setReload(true) }; // You might be able to call API directly here, I haven't tried
  const [debouncedCallApi] = useState(() => _.debounce(callApi, 1000));

  function handleChange() { 
    debouncedCallApi(); 
  }

  return (<>
    <input onChange={handleChange} />
  </>);
}

Solution 4

A lot of the answers here I found to be overly complicated or just inaccurate (i.e. not actually debouncing). Here's a straightforward solution with a check:

const [count, setCount] = useState(0); // simple check debounce is working
const handleChangeWithDebounce = _.debounce(async (e) => {
    if (e.target.value && e.target.value.length > 4) {
        // TODO: make API call here
        setCount(count + 1);
        console.log('the current count:', count)
    }
}, 1000);
<input onChange={handleChangeWithDebounce}></input>

Solution 5

That's not so easy question

On one hand to just work around error you are getting, you need to wrap up you setVariables in the function:

 search(e){
  let str = e.target.value;
  _.debounce(() => this.props.relay.setVariables({ query: str }), 500);
}

On another hand, I belive debouncing logic has to be incapsulated inside Relay.

Share:
91,866
Michael Kaufman
Author by

Michael Kaufman

Updated on October 04, 2021

Comments

  • Michael Kaufman
    Michael Kaufman over 2 years

    I'm trying to add debouncing with lodash to a search function, called from an input onChange event. The code below generates a type error 'function is expected', which I understand because lodash is expecting a function. What is the right way to do this and can it be done all inline? I have tried nearly every example thus far on SO to no avail.

    search(e){
     let str = e.target.value;
     debounce(this.props.relay.setVariables({ query: str }), 500);
    },
    
  • Michael Kaufman
    Michael Kaufman about 8 years
    Thanks for that. What I'm seeing now is a console log of the synthetic event and I need the e.target.value to perform the search.. I've tried e.persist() but it doesn't seem to do anything. Debouncing is technically working but without passing it a value it's not working. Thanks for any help.
  • Michael Kaufman
    Michael Kaufman about 8 years
    I couldn't use that exactly, but it got me where I needed to go. I basically had the input call search(e) and then passed that event to another function with the debouncing. I read about event.persist() but I couldn't get that work. Thanks for your help!!
  • Ezeewei
    Ezeewei over 6 years
    @Jeff Wooden fidden is broken
  • Rajesh Mbm
    Rajesh Mbm about 6 years
    thanks for suggesting componentWillMount. able to access props function as well in debounced function. if i put inside constructor, somehow i am not able to access props functions.
  • Jeff Wooden
    Jeff Wooden about 6 years
    @RajeshMbm You can access props inside of a class constructor, see the updated example - it's available as the first argument (make sure to include the super call).
  • CuteShaun
    CuteShaun almost 4 years
    useEffect trigger only one time, because reload after first call will be always true.
  • CuteShaun
    CuteShaun almost 4 years
    Try set your value to handleChange, than debouncedCallApi, then callApi -> state, after this useEffect trigger your function ^_^
  • ProblemSolver
    ProblemSolver about 3 years
    i think it calls n no of times , and just applies a delay as such, times , i tried it now at least.
  • Indigo
    Indigo about 3 years
    It is better to explain your answer, what it is doing, how it is solving OP's problem, give some references as required. Just adding a code block or a link is not enough. Check StackOverflow answer guide
  • Juanma Menendez
    Juanma Menendez almost 3 years
    you have a typo in the import: import _debouce from 'lodash/debounce'; It is NOT _debouce IT IS _debounce
  • Ashfaq nisar
    Ashfaq nisar over 2 years
    Thank you jules!
  • Fiddle Freak
    Fiddle Freak over 2 years
    it would be nice if we could see a solution using react hooks.
  • Fiddle Freak
    Fiddle Freak over 2 years
    just a warning, useCallback will set all variables with useState to the initial value of the page loaded. I ran into this bug, and am now trying to find another way to use the debouncer.
  • Fiddle Freak
    Fiddle Freak over 2 years
    I would avoid using useCallback with the debouncer. This will force all useState hooks to reset to the initial state from when the page was loaded.
  • Jules Patry
    Jules Patry over 2 years
    @FiddleFreak I have never experienced that. I think the issue is something else.
  • Fiddle Freak
    Fiddle Freak over 2 years
    @JulesPatry I actually got around that problem by doing this instead > stackoverflow.com/questions/70501416/…