获取最新值的 hook,但是不能避免对象的地址改变
// 每次渲染都将最新的value存到ref中,但是value如果是引用类型地址还是会变
function useLatest<T>(value: T) {
  const ref = useRef(value)
  ref.current = value
  return ref
}获取最新更新了状态的最新函数的 hook,而且函数地址不变,理论上可以替换 useCallback
type noop = (this: any, ...args: any[]) => any
// https://github.com/alibaba/hooks/pull/1470
type PickFunction<T extends noop> = (
  this: ThisParameterType<T>,
  ...args: Parameters<T>
) => ReturnType<T>
// 返回更新了状态但是地址不变的fn
function useMemoizedFn<T extends noop>(fn: T) {
  // 开发环境日志
  if (isDev) {
    if (!isFunction(fn)) {
      console.error(
        `useMemoizedFn expected parameter is a function, got ${typeof fn}`,
      )
    }
  }
  // fnRef因为适配所以分开写
  // 本质就是fnRef=useLatest(fn)
  // 注意fnRef.current的地址会改变
  // 使用useRef保存最新的fn
  const fnRef = useRef<T>(fn)
  // 适配 react devtool
  // why not write `fnRef.current = fn`?
  // https://github.com/alibaba/hooks/issues/728
  fnRef.current = useMemo(() => fn, [fn])
  // 初始化memoizedFn,memoizedFn.current的函数地址不会改变
  const memoizedFn = useRef<PickFunction<T>>()
  // 只有初始化时赋值
  if (!memoizedFn.current) {
    // 这里的第一个参数this是适配eslint的eslint no-invalid-this https://github.com/alibaba/hooks/pull/1464
    // 这里是防止函数地址改变才包裹一层function
    // 防止函数this指向丢失使用apply调用
    memoizedFn.current = function (this, ...args) {
      return fnRef.current.apply(this, args)
    }
  }
  // 返回的memoizedFn.current始终是通过function包裹的fn.current,fn.current保证是最新的函数
  return memoizedFn.current as T
}