# react-hook-awaited

> wrapper hook for awaiting promises

Latest version **1.1.1** (published 2022-08-14) · MIT license · 0 weekly downloads

## Install

```sh
npm install react-hook-awaited
pnpm add react-hook-awaited
yarn add react-hook-awaited
bun add react-hook-awaited
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.1.1 |
| Published | 2022-08-14 |
| First published | 2020-12-06 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 0 |
| Unpacked size | 11.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | David Evans |
| Maintainers | davidje13 |
| Keywords | react, hook, await, async, promise |

## Links

- npm: https://www.npmjs.com/package/react-hook-awaited
- Repository: https://github.com/davidje13/react-hook-awaited
- Homepage: https://github.com/davidje13/react-hook-awaited#readme
- Issues: https://github.com/davidje13/react-hook-awaited/issues
- npm.io page: https://npm.io/package/react-hook-awaited

## Alternatives

- [@commercetools/sync-actions](https://npm.io/package/@commercetools/sync-actions.md) — 25.1K weekly downloads
- [cwait](https://npm.io/package/cwait.md) — 21.4K weekly downloads
- [@ledgerhq/hw-app-cosmos](https://npm.io/package/@ledgerhq/hw-app-cosmos.md) — 4.2K weekly downloads
- [@financial-times/o-loading](https://npm.io/package/@financial-times/o-loading.md) — 2.8K weekly downloads
- [fa](https://npm.io/package/fa.md) — 185 weekly downloads

## Recent versions

- 1.1.1 (latest) — 2022-08-14
- 1.1.0 — 2022-08-12
- 1.0.0 — 2020-12-06

## README

# React useAwaited hook

A helper for working with asynchronous data in react functional components.

See the [examples](#examples) for some use cases.

## Install dependency

```bash
npm install --save react-hook-awaited
```

## Usage

```jsx
const useAwaited = require('react-hook-awaited');

const MyComponent = () => {
  const apiUrl = 'https://xkcd.com/info.0.json';
  const apiResponse = useAwaited((signal) => fetch(apiUrl, { signal }).then((r) => r.json()), [apiUrl]);

  switch (apiResponse.state) {
    case 'pending':
      return (<div>Loading...</div>);
    case 'resolved':
      return (<div>Latest: {apiResponse.data.num}</div>);
    case 'rejected':
      return (
        <div>
          Failed: {apiResponse.error}
          <button onClick={apiResponse.forceRefresh()}>Try Again</button>
        </div>
      );
  }
};
```

If you are using [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks),
you should configure it to check dependencies for `useAwaited` and
`useAwaitedWithDefault`:

```json
"react-hooks/exhaustive-deps": ["warn", {
  "additionalHooks": "(useAwaited|useAwaitedWithDefault)"
}]
```

Alternatively, you can provide functions using `useCallback` yourself
(this requires more syntax but avoids the need to reconfigure the linter):

```js
const apiResponse = useAwaited(
  useCallback(
    (signal) => fetch(apiUrl, { signal }).then((r) => r.json()),
    [apiUrl]
  )
);
```

## API

### useAwaited(generatorFunction, deps)

```javascript
const value = useAwaited(generatorFunction, deps);
```

Invokes the `generatorFunction` and returns the state of the returned
promise.

- `generatorFunction`: a function which returns a promise which is
  to be awaited. It is passed a single argument: an
  [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
  which will be marked as aborted if a change means that the current
  request is no-longer required.
- `deps`: a list of dependencies for the `generatorFunction`, matching
  the same API as
  [`React.useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback).

The `AbortSignal` can be passed directly to `fetch` calls to avoid
leaving requests running which are no-longer required:

```js
useAwaited((abortSignal) => fetch('https://example.com', { signal: abortSignal }), []);
```

**note:** If the `deps` change, the current promise will be discarded,
the `AbortSignal` will be triggered, `generatorFunction` will be called
again, and the newly returned promise will be awaited.

If you do not provide `deps`, the default is to re-invoke the generator
whenever the generator function changes. This means you can pass a
`useCallback`-wrapped function _instead of_ providing `deps`.

#### Return value

The response is an object which contains several properties:

- `state`: one of `'pending'`, `'resolved'` or `'rejected'`. For
  convenience these are also exported as constants `PENDING`,
  `RESOLVED`, `REJECTED`.
- `data`: the current data returned by the promise (if `state` is
  `'resolved'`, otherwise `undefined`).
- `error`: the current error returned by the promise (if `state` is
  `'rejected'`, otherwise `undefined`).
- `stats`: an object containing statistics about the current promise:
  - `beginTimestamp`: time when the promise began (number of
    milliseconds since the epoch)
  - `endTimestamp`: time when the promise completed (number of
    milliseconds since the epoch), or undefined if `state` is
    `pending`.
- `latestData`: the last successfully resolved data. Unlike `data`,
  this continues to be available until new data replaces it.
  This is `undefined` until the first request succeeds.
- `latestStats`: an object containing statistics about the last
  completed promise. Unlike `stats`, this continues to be available
  while new data is loaded. This is `undefined` until the first
  request has completed.
- `forceRefresh`: a function which can be called to force an
  immediate refresh of the data. This function is guaranteed to be
  stable (will be the same function instance across all renders).

### useAwaitedWithDefault(default, generatorFunction, deps)

Same as `useAwaited`, but `latestData` will be initialised as
`default` rather than `undefined`.

## Examples

### Loading data from a dynamic API endpoint

```jsx
const useAwaited = require('react-hook-awaited');

const ComicViewer = () => {
  const [num, setNum] = useState(1);
  const apiUrl = `https://xkcd.com/${num}/info.0.json`;
  const apiResponse = useAwaited((signal) => fetch(apiUrl, { signal }).then((r) => r.json()), [apiUrl]);

  let content;
  if (apiResponse.state === 'pending') {
    content = (<div>Loading...</div>);
  } else if (apiResponse.state === 'rejected') {
    content = (
      <div>
        Failed to load #{num}: {apiResponse.error}
        <br />
        <button onClick={apiResponse.forceRefresh()}>Try Again</button>
      </div>
    );
  } else {
    content = (
      <div>
        <h1>{apiResponse.data.title}</h1>
        <img src={apiResponse.data.img} alt={apiResponse.data.alt} />
      </div>
    );
  }
  return (
    <section>
      <label>Show XKCD <input type="number" value={num} onChange={setNum} /></label>
      { content }
    </section>
  );
};
```

### Show latest data with user-controlled refresh

```jsx
const useAwaited = require('react-hook-awaited');

const DataFetcher = () => {
  const apiUrl = 'https://xkcd.com/info.0.json';
  const apiResponse = useAwaited((signal) => fetch(apiUrl, { signal }).then((r) => r.json()), [apiUrl]);

  let content = null;
  if (apiResponse.latestData) {
    content = (
      <div>
        <p>Latest: {apiResponse.latestData.num}</p>
        <p>(as of ${new Date(apiResponse.latestStats.endTimestamp).toString()})</p>
      </div>
    );
  }
  return (
    <section>
      {content}
      {apiResponse.state === 'pending' ? (
        <p>Refreshing...</p>
      ) : (
        <button onClick={apiResponse.forceRefresh()}>Refresh</button>
      )}
      {apiResponse.state === 'rejected' ? (
        <p>Failed to refresh: ${apiResponse.error}</p>
      ) : null}
    </section>
  );
};
```

### Automatically refreshing on an interval

This also uses [react-hook-final-countdown](https://github.com/davidje13/react-hook-countdown)

```jsx
const useAwaited = require('react-hook-awaited');
const {useTimeInterval} = require('react-hook-final-countdown');

const DataFetcher = () => {
  const apiUrl = 'https://xkcd.com/info.0.json';
  const time = useTimeInterval(1000 * 60 * 60); // update every hour
  const apiResponse = useAwaited((signal) => fetch(apiUrl, { signal }).then((r) => r.json()), [apiUrl, time]);

  return (
    <section>
      <p>Latest: {apiResponse.latestData?.num}</p>
      <p>(as of ${new Date(apiResponse.latestStats?.endTimestamp).toString()})</p>
    </section>
  );
};
```

---
_Source: https://npm.io/package/react-hook-awaited · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
