Reputation: 1903
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
at the point of the useCallback statement. 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), []);
//const delayedQuery = useCallback(debounce(updateQuery, 500), [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).
Other variations have are shown in comments, but all lead to the same error.
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.
Edit
The message reads to me as though debounce isn't recognized as being a function. I receive no indications of issues attempting to import debounce using either import { debounce } from 'lodash/has';
or import { debounce } from 'lodash/fp';
Upvotes: 0
Views: 1077
Reputation: 1903
The issue was importing the wrong lodash function. Specifically, I was originally importing:
import { debounce } from 'lodash/fp';
which (from my partial understanding) is a wrapper that better applies to functional programming techniques.
In this case, what I needed was to import:
import { debounce } from 'lodash';
Upvotes: 1
Reputation: 698
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:
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.debounce
d method.debounce
d method, according to the lodash docs, can either be called immediately, cancel
ed or flush
ed.This leads us to the following in your useEffect
:
delayedQuery
is a callback.delayedQuery()
, where the hook invokes the arrow function.debounce
and returns its value — the debounced updateQuery
!delayedQuery.cancel
.delayedQuery.cancel == undefined
, you get the mentioned TypeError
.Upvotes: 1
Reputation: 191976
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), []);
Upvotes: 2