Skip to content

Commit

Permalink
feat: useLockFn
Browse files Browse the repository at this point in the history
  • Loading branch information
awmleer committed Nov 2, 2020
1 parent 76d6554 commit 5aa5dc6
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 82 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ lib
node_modules
.history
.idea
.vscode
coverage
.doc
.DS_Store
Expand Down
164 changes: 82 additions & 82 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import useReactive from './useReactive';
import useFavicon from './useFavicon';
import useCountDown from './useCountDown';
import useWebSocket from './useWebSocket';
import useLockFn from './useLockFn';

const useControlledValue: typeof useControllableValue = function (...args) {
console.warn(
Expand Down Expand Up @@ -121,4 +122,5 @@ export {
useFavicon,
useCountDown,
useWebSocket,
useLockFn,
};
41 changes: 41 additions & 0 deletions packages/hooks/src/useLockFn/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useRef } from 'react';
import useLockFn from '../index';
import { sleep } from '../../utils/testingHelpers';

describe('useLockFn', () => {
it('should be defined', () => {
expect(useLockFn).toBeDefined();
});

const setUp = (): any =>
renderHook(() => {
const countRef = useRef(0);
const locked = useLockFn(async (step: number) => {
countRef.current += step;
await sleep(50);
});

return {
locked,
countRef,
};
});

it('should work', async () => {
const hook = setUp();
const { locked, countRef } = hook.result.current;
locked(1);
expect(countRef.current).toBe(1);
locked(2);
expect(countRef.current).toBe(1);
await sleep(30);
locked(3);
expect(countRef.current).toBe(1);
await sleep(30);
locked(4);
expect(countRef.current).toBe(5);
locked(5);
expect(countRef.current).toBe(5);
});
});
37 changes: 37 additions & 0 deletions packages/hooks/src/useLockFn/demo/demo1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* title: Prevent duplicated submits
* desc: Before the `submit` function finishes, the other click actions will be ignored.
*
* title.zh-CN: 防止重复提交
* desc.zh-CN: 在 `submit` 函数执行完成前,其余的点击动作都会被忽略。
*/

import React, { useState } from 'react';
import { message } from 'antd';
import { useLockFn } from 'ahooks';

function mockApiRequest() {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 2000);
});
}

export default () => {
const [count, setCount] = useState(0);

const submit = useLockFn(async () => {
message.info('Start to submit');
await mockApiRequest();
setCount((val) => val + 1);
message.success('Submit finished');
});

return (
<>
<p>Submit count: {count}</p>
<button onClick={submit}>Submit</button>
</>
);
};
40 changes: 40 additions & 0 deletions packages/hooks/src/useLockFn/index.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: useLockFn
nav:
title: Hooks
path: /hooks
group:
title: Advanced
path: /advanced
---

# useLockFn

Add lock to an async function to prevent parallel executions.

## Examples

### Prevent duplicated submits

<code src="./demo/demo1.tsx" />

## API

```typescript
function useLockFn<P extends any[] = any[], V extends any = any>(
fn: (...args: P) => Promise<V>
): fn: (...args: P) => Promise<V | undefined>
```

### Result

| 参数 | 说明 | 类型 |
|------|---------------------------|---------------------------|
| fn | The async function with lock | `(...args: any[]) => any` |
### Params
| 参数 | 说明 | 类型 | 默认值 |
|----------------|------------------|---------------------------|--------|
| fn | An async function | `(...args: any[]) => any` | - |
20 changes: 20 additions & 0 deletions packages/hooks/src/useLockFn/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useRef } from 'react';

function useLockFn<P extends any[] = any[], V extends any = any>(fn: (...args: P) => Promise<V>) {
const lockRef = useRef(false);

return async function (...args: P) {
if (lockRef.current) return;
lockRef.current = true;
try {
const ret = await fn(...args);
lockRef.current = false;
return ret;
} catch (e) {
lockRef.current = false;
throw e;
}
};
}

export default useLockFn;
40 changes: 40 additions & 0 deletions packages/hooks/src/useLockFn/index.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: useLockFn
nav:
title: Hooks
path: /hooks
group:
title: Advanced
path: /advanced
---

# useLockFn

用于给一个异步函数增加竞态锁,防止并发执行。

## 代码演示

### 避免重复提交

<code src="./demo/demo1.tsx" />

## API

```typescript
function useLockFn<P extends any[] = any[], V extends any = any>(
fn: (...args: P) => Promise<V>
): fn: (...args: P) => Promise<V | undefined>
```

### Result

| 参数 | 说明 | 类型 |
|------|---------------------------|---------------------------|
| fn | 增加了竞态锁的函数 | `(...args: any[]) => any` |

### Params

| 参数 | 说明 | 类型 | 默认值 |
|----------------|------------------|---------------------------|--------|
| fn | 需要增加竞态锁的函数 | `(...args: any[]) => any` | - |

0 comments on commit 5aa5dc6

Please sign in to comment.