This is my own hook which is used to realize debounce in React Hooks
import { useMemo, useRef } from "react";
import { debounce } from "lodash";
export function useDebounceFn(fn, wait) {
const fnRef = useRef(fn)
fnRef.current = () => {
debugger
fn()
}
const debounceFn = useMemo(() => {
// Case One: can get the newest fn everytime
return debounce(() => {
fnRef.current()
});
// Case Two: only can get the first fn
return debounce(fnRef.current);
}, []);
return {
run: debounceFn,
cancel: debounceFn.cancel,
}
}
I think debounce(fnRef.current) is equal to debounce(() => { fnRef.current() }) ,but the truth is that they are different and i wonder why. Is there any wrong with my code or just useMemo do something different in these two cases.
const { run } = useDebounceFn(() => {
console.log("num", num);
});
useEffect(() => {
run();
}, [num]);
When i use case one, i can get the newest num and this is what i want to realize, but when i use case two, everytime run function only can get the inital num. i want to know why the case two only can get the inital value, in others word, why the case one can always get the newest value in useMemo.
I think
debounce(fnRef.current)is equal todebounce(() => { fnRef.current() }), but the truth is that they are different and I wonder why.
run function is invoked, the current ref value is "unpacked".You could fix case 2 by populating the useMemo hook's dependency array to re-enclose the current ref value.
Example:
export function useDebounceFn(fn, wait) {
const fnRef = useRef(fn);
const debounceFn = useMemo(() => {
// Re-"cache" the function
fnRef.current = () => {
debugger
fn()
}
// Re-decorate with debounce HOF
return debounce(fnRef.current);
}, [fn]);
return {
run: debounceFn,
cancel: debounceFn.cancel,
}
}
BTW, for case 1 you should use an useEffect hook to set the ref value when the fn updates.
Example:
export function useDebounceFn(fn, wait) {
const fnRef = useRef(fn);
useEffect(() => {
fnRef.current = () => {
debugger
fn();
}
}, [fn]);
const debounceFn = useMemo(() => {
return debounce(() => {
fnRef.current();
});
}, []);
return {
run: debounceFn,
cancel: debounceFn.cancel,
}
}