My point of view as stated in the comments, that this hook is redundant in terms of "how many renders you get", when there are too frequent dependencies changes (in useEffect
/useCallback
dep arrays), using a normal function is the best option (no overhead).
This hook hiding the render of the component using it, but the render comes from the useEffect
in its parent.
If we summarize the render count we get:
- Ref + useCallback (the hook): Render in
Component
(due to value
) + Render in hook (useEffect
), total of 2.
- useCallback only: Render in
Component
(due to value
) + render in Counter
(change in function reference duo to value
change), total of 2.
- normal function: Render in
Component
+ render in Counter
: new function every render, total of 2.
But you get additional overhead for shallow comparison in useEffect
or useCallback
.
Practical example:
function App() {
const [value, setValue] = useState("");
return (
<div>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
type="text"
/>
<Component value={value} />
</div>
);
}
function useLastVersion(func) {
const ref = useRef();
useEffect(() => {
ref.current = func;
console.log("useEffect called in ref+callback");
}, [func]);
return useCallback((...args) => {
return ref.current(...args);
}, []);
}
function Component({ value }) {
const f1 = useLastVersion(() => {
alert(value.length);
});
const f2 = useCallback(() => {
alert(value.length);
}, [value]);
const f3 = () => {
alert(value.length);
};
return (
<div>
Ref and useCallback:{" "}
<MemoCounter callBack={f1} msg="ref and useCallback" />
Callback only: <MemoCounter callBack={f2} msg="callback only" />
Normal: <MemoCounter callBack={f3} msg="normal" />
</div>
);
}
function Counter({ callBack, msg }) {
console.log(msg);
return <button onClick={callBack}>Click Me</button>;
}
const MemoCounter = React.memo(Counter);

As a side note, if the purpose is only finding the length of input
with minimum renders, reading inputRef.current.value
would be the solution.