Home > Enterprise >  What causes lodash debouncer "TypeError: Expected a function" error?
What causes lodash debouncer "TypeError: Expected a function" error?

Time:01-08

I'm attempting to use lodash's debouncer method in a react function component. When I attempt to create the debouncer-wrapped callback, I receive

TypeError: Expected a function

I've attempted to work through three or four examples, but none of them work within my existing app. The latest attempt I'm working through is here.

  const [userQuery, setUserQuery] = useState("")
  const [deb, setDeb] = useState("");

  const updateQuery = () => {
      setDeb(userQuery)
  };
  
  const delayedQuery = useCallback(() => debounce(updateQuery, 500), [userQuery]);
  
  const onChange = e => {
     setUserQuery(e.target.value);
  };
  
  useEffect(() => {
     delayedQuery();
  
     // Cancel the debounce on useEffect cleanup.
     return delayedQuery.cancel;
  }, [userQuery, delayedQuery]);

The code functions overall without the debounce wrapper and related return in the useEffect (without any debouncing, of course).

I'm at a level of modestly comfortable with React so have a lot of gaps in my deeper understanding that could be the problem.

CodePudding user response:

When you call delayedQuery() you return the debounced function, and not the result of calling the debounced function. Since debounce returns a debounced function with the cancel method, and useCallback accepts a function, define delayedQuery like this:

const delayedQuery = useCallback(debounce(updateQuery, 500), []);

CodePudding user response:

I assume the mentioned TypeError was thrown during the useEffect, since you have not provided any Stack Trace.

Edit thanks to the comment and suggestion by @Ori Drori: Change the delayedQuery to the following:

const delayedQuery = useCallback(debounce(updateQuery, 500), []);

This way, you will have a memoized value of your debounced method, which will be a callback anyway, with the available cancel property.


Previous Answer (just for reference):

Then you can try changing your useEffect to the following:

useEffect(() => {
  const query = delayedQuery();
  query();
  
  // Cancel the denounce on useEffect cleanup
  return query.cancel;
}, [userQuery, delayedQuery]);

Explanation

You can have a look at the different types of your methods and callbacks. I‘ll start at the beginning:

  1. useCallback simply takes any method and returns a new method, which will simply invoke the provided method. It will match the signature of the provided method.
  2. The provided callback, in turn, is a simple arrow function, which returns a debounced method.
  3. The debounced method, according to the lodash docs, can either be called immediately, canceled or flushed.

This leads us to the following in your useEffect:

  • delayedQuery is a callback.
  • You call delayedQuery(), where the hook invokes the arrow function.
  • The arrow function invokes the debounce and returns its value — the debounced updateQuery!
  • BUT: at this point, your query has not yet been invoked! Moreover, since only the return type of your callback is a debounced function, there is no property delayedQuery.cancel.
  • And because delayedQuery.cancel == undefined, you get the mentioned TypeError.
  •  Tags:  
  • Related