I have hooks that accept functions as arguments. If the functions change unnecessarily, then it could cause extra re-renders. I want to ensure that the functions are wrapped with useCallback.

I.e. here’s a simplified version:

function useApi({ onSuccess }) {
  if (process.env.NODE_ENV !== 'production' && !isUseCallback(onSuccess)) {
    throw new Error();
  }

  useEffect(() => {
    fetch(...)
      .then(res => res.json())
      .then(onSuccess);
  }, [onSuccess]);
}

If onSuccess changes unnecessarily, then it’ll call the API unnecessarily. Is it possible to have the function isUseCallback? I think I’d have to write a custom useCallback that wraps React’s useCallback.

Answer

I solved this using Typescript:

type UseCallback<T extends (...args: any[]) => any> =
  ((...args: Parameters<T>) => ReturnType<T>) & { __IS_USE_CALLBACK?: undefined };

declare function useCallback<T extends (...args: any[]) => any>(
  callback: T,
  deps: ReadonlyArray<any>,
): UseCallback<T>;

.

function useApi(onSuccess: UseCallback<() => any>): any;

useApi(() => {}); // Error
useApi(useCallback(() => {})); // No error