DEV Community

Discussion on: The Botched Implementation of useEffect()

Collapse
 
peerreynders profile image
peerreynders

Seems to me useCallback could have benefitted from some additional Hammock time as well.

// useCallback avoids the changing props value
// issue of newly created functions that don't behave
// differently - but is doesn't address creating garbage
// (i.e. unnecessary) functions on the majority of calls
//
const stepDelta = useCallback(() => setDelta(incDelta), []);
const stepCount = useCallback(() => setCount(count => count + delta), [delta]);

In terms of design I would have expected something more along these lines:

// custom hook
function useRefreshCallback(refreshCallback, isEqual, deps) {
  const [state, setCallback] = useState(null);

  if (state !== null && isEqual(state.lastDeps, deps)) {
    return state.callback;
  }

  const fresh = refreshCallback(deps);
  setCallback({callback: fresh, lastDeps: deps});
  return fresh;
}

Example usage:

// Component Parts
const initialDelta = 1;
const incDelta = delta => delta + 1;
const refreshDeltaCallback = ([setDelta]) => {
  console.log('refresh delta callback');
  return () => setDelta(incDelta);
};

const initialCount = 0;
const refreshCountCallback = ([delta, setCount]) => {
  console.log('refresh count callback with delta:', delta);
  const incCount = count => count + delta;
  return () => setCount(incCount);
};

// ...

// Component
// contrived use of useRefreshCallback - "cost of checks" yada, yada
function App() {
  const [delta, setDelta] = useState(initialDelta);
  const [count, setCount] = useState(initialCount);

  // call `refreshCallback` function only if dependencies change
  const stepDelta = useRefreshCallback(refreshDeltaCallback, depsEqual, [setDelta]);
  const stepCount = useRefreshCallback(refreshCountCallback, depsEqual, [delta, setCount]);

Gist: useRefreshCallback - custom hook that also avoids creating garbage functions.