useAsyncCallback

This commit is contained in:
Ward Oosterlijnck 2019-04-09 00:18:07 +10:00
parent a48d57cccb
commit 22682c176c
5 changed files with 122 additions and 0 deletions

View File

@ -73,6 +73,7 @@
<br/>
- [**Side-effects**](./docs/Side-effects.md)
- [`useAsync`](./docs/useAsync.md) &mdash; resolves an `async` function.
- [`useAsyncCallback`](./docs/useAsyncCallback.md) &mdash; state management for async callback
- [`useAsyncRetry`](./docs/useAsyncRetry.md) &mdash; `useAsync` with `retry()` method.
- [`useCopyToClipboard`](./docs/useCopyToClipboard.md) &mdash; copies text to clipboard.
- [`useDebounce`](./docs/useDebounce.md) &mdash; debounces a function. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/side-effects-usedebounce--demo)

36
docs/useAsyncCallback.md Normal file
View File

@ -0,0 +1,36 @@
# `useAsyncCallback`
React hook that returns state and a callback for an `async` function or a
function that returns a promise. The state is of the same shape as `useAsync`.
## Usage
```jsx
import {useAsyncCallback} from 'react-use';
const Demo = (url) => {
const [state, fetch] = useAsyncCallback(async () => {
const response = await fetch(url);
const result = response.text();
return result
}), [url];
return (
<div>
{state.loading
? <div>Loading...</div>
: state.error
? <div>Error: {state.error.message}</div>
: state.value && <div>Value: {state.value}</div>
}
<button onClick={() => fetch()}>Start loading</button>
</div>
);
};
```
## Reference
```ts
useAsyncCallback(fn, deps?: any[]);
```

View File

@ -0,0 +1,35 @@
import * as React from 'react';
import {storiesOf} from '@storybook/react';
import {useAsyncCallback} from '..';
import ShowDocs from './util/ShowDocs';
const fn = () =>
new Promise<string>((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
reject(new Error('Random error!'));
} else {
resolve('RESOLVED');
}
}, 1000);
});
const Demo = () => {
const [{loading, error, value}, callback] = useAsyncCallback<string>(fn);
return (
<div>
{loading
? <div>Loading...</div>
: error
? <div>Error: {error.message}</div>
: value && <div>Value: {value}</div>
}
<button onClick={() => callback()}>Start</button>
</div>
);
};
storiesOf('Side effects|useAsyncCallback', module)
.add('Docs', () => <ShowDocs md={require('../../docs/useAsyncCallback.md')} />)
.add('Demo', () => <Demo/>)

View File

@ -1,5 +1,6 @@
import createMemo from './createMemo';
import useAsync from './useAsync';
import useAsyncCallback from './useAsyncCallback';
import useAsyncRetry from './useAsyncRetry';
import useAudio from './useAudio';
import useBattery from './useBattery';
@ -71,6 +72,7 @@ import useUpdateEffect from './useUpdateEffect'
export {
createMemo,
useAsync,
useAsyncCallback,
useAsyncRetry,
useAudio,
useBattery,

48
src/useAsyncCallback.ts Normal file
View File

@ -0,0 +1,48 @@
import { useState, useEffect, useCallback, DependencyList } from 'react';
import useRefMounted from "./useRefMounted"
export type AsyncState<T> =
| {
loading: boolean;
error?: undefined;
value?: undefined;
}
| {
loading: false;
error: Error;
value?: undefined;
}
| {
loading: false;
error?: undefined;
value: T;
};
const useAsyncCallback = <T>(fn: () => Promise<T>, deps: DependencyList = []): [AsyncState<T>, () => void] => {
const [state, set] = useState<AsyncState<T>>({
loading: false
});
const mounted = useRefMounted();
const callback = useCallback(() => {
set({loading: true});
fn().then(
value => {
if (mounted.current) {
set({value, loading: false});
}
},
error => {
if (mounted.current) {
set({error, loading: false});
}
}
);
}, deps);
return [state, callback]
};
export default useAsyncCallback;