Skip to content

Commit

Permalink
feat: 🎸 add usePromise() hook
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Mar 15, 2019
1 parent a2a6fe3 commit aad368b
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
- [**Lifecycles**](./docs/Lifecycles.md)
- [`useLifecycles`](./docs/useLifecycles.md) — calls `mount` and `unmount` callbacks.
- [`useRefMounted`](./docs/useRefMounted.md) — tracks if component is mounted.
- [`usePromise`](./docs/usePromise.md) — resolves promise only while component is mounted.
- [`useLogger`](./docs/useLogger.md) — logs in console as component goes through life-cycles.
- [`useMount`](./docs/useMount.md) — calls `mount` callbacks.
- [`useUnmount`](./docs/useUnmount.md) — calls `unmount` callbacks.
Expand Down
24 changes: 24 additions & 0 deletions docs/usePromise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# `usePromise`

React Lifecycle hook that returns a helper function for wrapping promises.
Promises wrapped with this function will resolve only when component is mounted.


## Usage

```jsx
import {usePromise} from 'react-use';

const Demo = ({promise}) => {
const mounted = usePromise();
const [value, setValue] = useState();

useEffect(() => {
(async () => {
const value = await mounted(promise);
// This line will not execute if <Demo> component gets unmounted.
setValue(value);
})();
});
};
```
42 changes: 42 additions & 0 deletions src/__stories__/usePromise.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {storiesOf} from '@storybook/react';
import * as React from 'react';
import {usePromise, useBoolean, useNumber} from '..';
import ShowDocs from '../util/ShowDocs';

const {useState, useEffect} = React;

const DemoInner = ({promise}) => {
const safePromise = usePromise();
const [value, setValue] = useState<number>(-1);
useEffect(() => {
safePromise(promise).then(setValue);
}, [promise]);

return (
<div>
{value === -1 ? 'Resolving value...' : 'Value: ' + value}
</div>
);
};

const Demo = () => {
const [mounted, toggleMounted] = useBoolean(true);
const [num, {inc}] = useNumber();
const promise = new Promise(r => setTimeout(() => r(num), 1_000));

return (
<div>
<p>This demo provides a number in a promise that resolves in 1sec to a child component.</p>
<button onClick={() => toggleMounted()}>{mounted ? 'Unmount' : 'Mount'}</button>
<button onClick={() => inc()}>Increment ({num})</button>
<br />
{mounted && <DemoInner promise={promise} />}
</div>
);
};

storiesOf('Lifecycles|usePromise', module)
.add('Docs', () => <ShowDocs md={require('../../docs/usePromise.md')} />)
.add('Demo', () =>
<Demo/>
)
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import useNumber from './useNumber';
import useObservable from './useObservable';
import useOrientation from './useOrientation';
import useOutsideClick from './useOutsideClick';
import usePromise from './usePromise';
import useRaf from './useRaf';
import useRefMounted from './useRefMounted';
import useSessionStorage from './useSessionStorage';
Expand Down Expand Up @@ -80,6 +81,7 @@ export {
useObservable,
useOrientation,
useOutsideClick,
usePromise,
useRaf,
useRefMounted,
useSessionStorage,
Expand Down
22 changes: 22 additions & 0 deletions src/usePromise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {useCallback} from 'react';
import useRefMounted from './useRefMounted';

export type UsePromise = () => <T>(promise: Promise<T>) => Promise<T>;

const usePromise: UsePromise = () => {
const refMounted = useRefMounted();
return useCallback(<T>(promise: Promise<T>): Promise<T> =>
new Promise<T>((resolve, reject) => {
promise.then(value => {
if (refMounted.current) {
resolve(value);
}
}, error => {
if (refMounted.current) {
reject(error);
}
})
}), []);
};

export default usePromise;

0 comments on commit aad368b

Please sign in to comment.