dyo / dyo

Dyo is a JavaScript library for building user interfaces.

Home Page:https://dyo.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Function `useCallback` should have a dependency argument (like in React and Preact)

mcjazzyfunky opened this issue · comments

In difference to React and Preact in Dyo useCallback does not have a dependency argument.
The Dyo documentation states:

useCallback
[...]
Unlike useMemo the returned function is guarenteed to always be a "stable" function reference.

This sounds like a good feature first, but actually it isn't - as it violates one of the core principles of the whole hooks idea that the closures within the render functions always have access to the relevant values at the time the render function was called.
It's a bit difficult to explain what I mean, hopefully the following demo will help a bit:
https://codesandbox.io/s/stoic-shockley-ubo50

Of course you can just use useMemo instead for such cases - but why should you? Shouldn't callbacks not just always work with useCallback?

PS: This is the implementation of useCallback in Preact:

/**
 * @param {() => void} callback
 * @param {any[]} args
 */
export function useCallback(callback, args) {
  return useMemo(() => callback, args);
}

You make a fair point. We could delegate the current behaviour to only bare useCallback that do not provide dependencies like useCallback(() => {}), the same can be said for bare useMemo(() => {}) when dependencies are omitted given the no-op nature of that variant.

I strongly believe the best solution would be (like in Preact and React):

useCallback(callback) // return value will always change
useCallback(callback, []) // return value will never change
useCallback(callback, [...someValues]) // return value will change when dependencies change

Same for useMemo (as already is):

useMemo(computation) // return value will always change
useMemo(computation, []) // return value will never change
useMemo(computation, [...someValues]) // return value will change when dependencies change

So the second argument would have the same semantical meaning in useCallback, useMemo, useEffectetc

Why not

useCallback(callback) == useCallback(callback, []) // return value will never change
useMemo(computation) == useMemo(computation, []) // return value will never change

Having the value always change negates the hook entirely.

I think, that would be a bit inconsistent regarding the semantic of leaving out the second argument as it would be completely different to the semantic of leaving out the second argument for useEffect, wouldn't it?

PS: [Edit] Okay, I see: Why should you ever want that "will always change" case with useCallback? 🤔

PPS: [Edit] ESLint shows the following warning if you do not pass a second argument to useCallback (similar for useMemo):

React Hook useCallback does nothing when called with only one argument. Did you forget to pass an array of dependencies? (react-hooks/exhaustive-deps)eslint

Maybe an argument for useCallback(callback, []) and useMemo(calculation, []) would be at least that with this syntax and semantic the react-hooks ESLint plugin would also work for Dyo....

Exactly, both useMemo and useCallback should never be used without a second argument.
I think removing extraneous , [] everywhere has value.

I'm getting this warning, React Hook useCallback does nothing when called with only one argument. Did you forget to pass an array of dependencies?, but I DID pass a second argument. The dependencies array, empty, or with dependencies. Sill get the warning. It's driving me crazy

Dyo doesn't emit React warnings, probably an issue with a lint tool you are using.

I'm getting this warning, React Hook useCallback does nothing when called with only one argument. Did you forget to pass an array of dependencies?, but I DID pass a second argument. The dependencies array, empty, or with dependencies. Sill get the warning. It's driving me crazy

Pass in an empty array to fix the warning ->

const allowButtonHandler = useCallback(() => { setAllowToggle(true) }, [])