Skip to content

Commit 2234105

Browse files
committed
chore: add useUntil
1 parent 3ec6fee commit 2234105

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export { default as useDeepEffect } from './useDeepEffect';
1616
export { default as useInterval } from './useInterval';
1717
export { default as useTimeout } from './useTimeout';
1818
export { default as useTimeoutFn } from './useTimeoutFn';
19+
export { default as useUntil } from './useUntil';
1920

2021
export { default as useSet } from './useSet';
2122
export { default as useMap } from './useMap';

src/hooks/useUntil.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React from 'react';
2+
3+
import { until } from '@planjs/utils';
4+
5+
import type { BaseRetryOption } from '@planjs/utils/typings/promise/interfaces';
6+
import type { ThenReturn } from '@planjs/utils/es/type';
7+
8+
function useUntil<T extends (...args: any[]) => Promise<any>>(fn: T, options: BaseRetryOption) {
9+
const isRunning = React.useRef(false);
10+
const forceStop = React.useRef(false);
11+
12+
const forceStopCallback = React.useRef<(val?: any) => void>();
13+
14+
const [result, setResult] = React.useState<ThenReturn<T>>();
15+
const [err, setErr] = React.useState<Error | undefined>();
16+
17+
const action = React.useRef(
18+
until(async (props) => {
19+
if (forceStop.current) {
20+
forceStopCallback.current && forceStopCallback.current();
21+
return forceStop.current;
22+
}
23+
try {
24+
setErr(undefined);
25+
const res = await fn(props);
26+
setResult(res);
27+
} catch (error) {
28+
setErr(error as Error);
29+
}
30+
}, options),
31+
);
32+
33+
const cancel = React.useCallback(() => {
34+
forceStop.current = true;
35+
return new Promise((resolve) => {
36+
if (isRunning.current) {
37+
forceStopCallback.current = resolve;
38+
} else {
39+
resolve(true);
40+
}
41+
})
42+
.then(() => (forceStop.current = false))
43+
.then(() => (isRunning.current = false));
44+
}, []);
45+
46+
const run = React.useCallback(
47+
async (props: any, options?: { once?: boolean }) => {
48+
if (isRunning.current) {
49+
await cancel();
50+
}
51+
isRunning.current = true;
52+
action.current(props);
53+
options?.once && cancel();
54+
},
55+
[cancel, action],
56+
);
57+
58+
return { result, run, cancel, err };
59+
}
60+
61+
export default useUntil;

0 commit comments

Comments
 (0)