3

I'm trying to write custom hooks but I'm facing an issue whenever I want function as arguments.

I want to write a useDebouce hook; From example around the web I ended up with this:

import { useCallback, useEffect } from 'react';
import debounce from 'lodash/debounce';

const useDebounce = (fn, time, options) => {
  useEffect(() => console.log('reset'), [fn, time, options]);
  return useCallback(debounce(fn, time, options), [fn, time, options]);
};

I want to reset whenever an argument change.

However if I use it like this:

const randomComponent = () => {
  const doSomething = useDebounce(() => console.log('test'), 500, {maxWait: 1000});
  // ...
};

Every time my component renders the function and object reference change (1st and 3rd arguments), which means I end up creating a new debounced function everytime. So the debounce behavior doesn't work.

What is the best way to deal with callback references changing at every render ?

1 Answer 1

1

There is no way out, the user must make sure it has the same reference:

import { useCallback, useEffect } from 'react';
import debounce from 'lodash/debounce';

const useDebounce = (fn, time, options) => {
  useEffect(() => {
    // Don't forget to cancel the debounce
    return () => debounce(fn).cancel();
  }, [fn, time, options]);

  return useCallback(debounce(fn, time, options), [fn, time, options]);
};

const log = () => console.log('test');
const maxWait = { maxWait: 1000 };

// Outer scope holds the same reference
const randomComponent = () => {
  const doSomething = useDebounce(log, 500, maxWait);
};

// Or memoization, more usefull with state
const randomComponent = () => {
  const log = useCallback(() => console.log('test'), []);
  const maxWait = useMemo(() => ({ maxWait: 1000 }), []);
  const doSomething = useDebounce(log, 500, maxWait);
};

Also, if you don't want to deal with references, the user might provide a comparison function. May suggest trying another approach:

// Something like that (not tested)
const useDebounce = (fn, time, options, comp) => {
  const debounceRef = useRef(debounce(fn, time, options));

  if (comp(fn, time, options)) {
    debounce(debounceRef.current).cancel();
    debounceRef.current = debounce(fn, time, options);
  }

  return useCallback(debounceRef.current, []);
};
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.