2024-01-10 17:01:48 +00:00
|
|
|
import { useMemo, useRef, useState } from 'react';
|
2023-09-02 18:52:41 +00:00
|
|
|
|
|
|
|
// From https://github.com/alibaba/hooks/blob/master/packages/hooks/src/useMemoizedFn/index.ts
|
|
|
|
|
|
|
|
type Noop = (this: any, ...args: any[]) => any;
|
|
|
|
|
|
|
|
type PickFunction<T extends Noop> = (
|
|
|
|
this: ThisParameterType<T>,
|
|
|
|
...args: Parameters<T>
|
|
|
|
) => ReturnType<T>;
|
|
|
|
|
|
|
|
export function useEvent<T extends Noop>(fn: T) {
|
|
|
|
if (process.env.NODE_ENV === 'development') {
|
|
|
|
if (typeof fn !== 'function') {
|
|
|
|
console.error(
|
|
|
|
`useMemoizedFn 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>>();
|
|
|
|
if (!memoizedFn.current) {
|
|
|
|
memoizedFn.current = function (this, ...args) {
|
|
|
|
return fnRef.current.apply(this, args);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return memoizedFn.current as T;
|
|
|
|
}
|
2024-01-10 17:01:48 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Same with useEvent but return loading state
|
|
|
|
*/
|
|
|
|
export function useEventWithLoading<T extends (...args: any[]) => Promise<any>>(
|
|
|
|
fn: T
|
|
|
|
): [T, boolean] {
|
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
|
|
|
|
const _fn = useEvent(async (...args: Parameters<T>) => {
|
|
|
|
setIsLoading(true);
|
|
|
|
try {
|
|
|
|
return await fn(...args);
|
|
|
|
} finally {
|
|
|
|
setIsLoading(false);
|
|
|
|
}
|
|
|
|
}) as T;
|
|
|
|
|
|
|
|
return [_fn as T, isLoading];
|
|
|
|
}
|