// https://github.com/msgbyte/tailchat/blob/master/client/shared/hooks/useMemoizedFn.ts
import { useMemo, useRef } from "react";

type Noop = (this: any, ...args: any[]) => any;

type PickFunction<T extends Noop> = (
  this: ThisParameterType<T>,
  ...args: Parameters<T>
) => ReturnType<T>;

export function useMemoFunc<T extends Noop>(fn: T) {
  if (process.env.NODE_ENV === "development") {
    if (typeof fn !== "function") {
      console.error(
        `useMemoFunc expected parameter is a function, got ${typeof fn}`,
      );
    }
  }

  const fnRef = useRef<T>(fn);

  // why not write `fnRef.current = fn`?
  // https://github.com/alibaba/hooks/issues/728
  fnRef.current = useMemo(() => fn, [fn]);

  const memoizedFn = useRef<PickFunction<T>>(function (this, ...args) {
    return fnRef.current.apply(this, args);
  });

  return memoizedFn.current as T;
}
