Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9,512 changes: 1,884 additions & 7,628 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
"devDependencies": {
"@babel/cli": "^7.10.1",
"@babel/core": "^7.10.2",
"@babel/preset-env": "^7.14.2",
"@babel/preset-react": "^7.13.13",
"@testing-library/react": "^10.0.4",
"@testing-library/react-hooks": "^2.0.1",
"@types/jest": "^24.9.1",
Expand All @@ -40,7 +42,7 @@
"babel-plugin-import": "^1.12.0",
"babel-plugin-transform-async-to-promises": "^0.8.15",
"del": "^5.1.0",
"dumi": "^1.1.7",
"dumi": "^1.1.18",
"enzyme": "^3.10.0",
"eslint": "^7.2.0",
"eslint-plugin-react-hooks": "^4.0.8",
Expand Down
296 changes: 60 additions & 236 deletions packages/hooks/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 @@ -59,6 +59,7 @@ import useTrackedEffect from './useTrackedEffect';
import useUnmountedRef from './useUnmountedRef';
import useExternal from './useExternal';
import useSafeState from './useSafeState';
import useAsyncEffect from './useAsyncEffect';

const useControlledValue: typeof useControllableValue = function (...args: any) {
console.warn(
Expand Down Expand Up @@ -132,4 +133,5 @@ export {
useUnmountedRef,
useExternal,
useSafeState,
useAsyncEffect,
};
62 changes: 62 additions & 0 deletions packages/hooks/src/useAsyncEffect/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { renderHook, act } from '@testing-library/react-hooks';
import useAsyncEffect from '../index';
import { useState } from 'react';
import { sleep } from '../../utils/testingHelpers';

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

it('should work without clean up', async () => {
const hook = renderHook(() => {
const [x, setX] = useState(0);
useAsyncEffect(async () => {
await sleep(100);
setX(1);
}, []);
return x;
});
expect(hook.result.current).toBe(0);
await act(async () => {
await sleep(150);
});
expect(hook.result.current).toBe(1);
});

it('should work with clean up', async () => {
const hook = renderHook(() => {
const [x, setX] = useState(1);
const [y, setY] = useState(0);
useAsyncEffect(
async (cleanUpWith) => {
let cancelled = false;
cleanUpWith(() => {
cancelled = true;
});
await sleep(100);
if (cancelled) return;
setY(x);
},
[x],
);
return {
y,
setX,
};
});
expect(hook.result.current.y).toBe(0);
await act(async () => {
await sleep(50);
hook.result.current.setX(2);
});
await act(async () => {
await sleep(80);
});
expect(hook.result.current.y).toBe(0);
await act(async () => {
await sleep(50);
});
expect(hook.result.current.y).toBe(2);
});
});
36 changes: 36 additions & 0 deletions packages/hooks/src/useAsyncEffect/demo/demo1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* title: Default usage
* desc: Do async check when component is mounted.
*
* title.zh-CN: 基础用法
* desc.zh-CN: 组件加载时进行异步的检查
*/

import React, { useState } from 'react';
import { useAsyncEffect } from 'ahooks';

function mockCheck(): Promise<boolean> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, 3000);
});
}

export default () => {
const [pass, setPass] = useState<boolean>(null);

useAsyncEffect(async () => {
setPass(await mockCheck());
}, []);

return (
<div>
<button disabled={!pass}>Submit</button>
<p>
{pass === null && 'Checking...'}
{pass === true && 'Check passed.'}
</p>
</div>
);
};
54 changes: 54 additions & 0 deletions packages/hooks/src/useAsyncEffect/demo/demo2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* title: Clean up
* desc: Handle the clean up logic of effect.
*
* title.zh-CN: 清理逻辑
* desc.zh-CN: 处理 effect 的清理逻辑
*/

import React, { useState } from 'react';
import { useAsyncEffect } from 'ahooks';

function mockCheck(val: string): Promise<boolean> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(val.length > 0);
}, 1000);
});
}

export default () => {
const [value, setValue] = useState('');
const [pass, setPass] = useState<boolean>(null);

useAsyncEffect(
async (cleanUpWith) => {
setPass(null);
let cancelled = false;
cleanUpWith(() => {
cancelled = true;
});
const result = await mockCheck(value);
if (cancelled) return;
setPass(result);
},
[value],
);

return (
<div>
<input
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
<button disabled={!pass}>Submit</button>
<p>
{pass === null && 'Checking...'}
{pass === false && 'Check failed.'}
{pass === true && 'Check passed.'}
</p>
</div>
);
};
30 changes: 30 additions & 0 deletions packages/hooks/src/useAsyncEffect/index.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: useAsyncEffect
nav:
title: Hooks
path: /hooks
group:
title: SideEffect
path: /side-effect
order: 7
---

# useAsyncEffect

Handle the async effect of components.

## 代码演示

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

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

## API

```typescript
type CleanUpWith = (cleanUp: () => void) => void;
function useAsyncEffect(
effect: (cleanUpWith: CleanUpWith) => Promise<void>,
deps: DependencyList
);
```
20 changes: 20 additions & 0 deletions packages/hooks/src/useAsyncEffect/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useEffect, DependencyList } from 'react';

type CleanUpWith = (cleanUp: () => void) => void;

function useAsyncEffect(effect: (cleanUpWith: CleanUpWith) => Promise<void>, deps: DependencyList) {
useEffect(() => {
let cleanUp: () => void;
function cleanUpWith(fn: () => void) {
cleanUp = fn;
}
(async function () {
await effect(cleanUpWith);
})();
return () => {
cleanUp?.();
};
}, deps);
}

export default useAsyncEffect;
30 changes: 30 additions & 0 deletions packages/hooks/src/useAsyncEffect/index.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
title: useAsyncEffect
nav:
title: Hooks
path: /hooks
group:
title: SideEffect
path: /side-effect
order: 7
---

# useAsyncEffect

用于处理异步的副作用。

## 代码演示

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

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

## API

```typescript
type CleanUpWith = (cleanUp: () => void) => void;
function useAsyncEffect(
effect: (cleanUpWith: CleanUpWith) => Promise<void>,
deps: DependencyList
);
```