Learn about our RFC process, Open RFC meetings & more.Join in the discussion! »

use-abortable-promise

1.1.2 • Public • Published

use-abortable-promise

Hook for managing abortable Promises (e.g. fetch()) inside React components.

yarn add use-abortable-promise

Usage

function App() {
  const [offset, setOffset] = useState(0);
 
  const [{ data, loading, error }, abort] = useAbortablePromise(
    async signal => {
      try {
        return await Promise.all([
          fetchUserById(offset + 1, { signal }),
          fetchUserById(offset + 2, { signal }),
          fetchUserById(offset + 3, { signal })
        ]);
      } catch (error) {
        if (error.message === 'Timeout') {
          abort();
        }
 
        throw error;
      }
    },
    [offset]
  );
 
  return (
    <>
      <button onClick={() => abort()}>Abort</button>
      <button onClick={() => setOffset(offset => offset + 1)}>
        Increase Offset ({offset})
      </button>
      <pre>{JSON.stringify({ data, loading, error }, null, 2)}</pre>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </>
  );
}

See more in the example app.

Composing New Hooks

The power of React Hooks let you compose and create even more customized hooks without a lot of effort. Take for example a useRest that wires up a fetch that automatically aborts on timeouts using use-abortable-promise.

import useAbortablePromise from 'use-abortable-promise';
 
function timeout(ms = 1000) {
  let timeoutId: any;
  return {
    start(): Promise<never> {
      return new Promise((_, reject) => {
        timeoutId = setTimeout(() => {
          reject(new TimeoutError('Timeout'));
        }, ms);
      });
    },
    clear() {
      clearTimeout(timeoutId);
    }
  };
}
 
async function fetchJson(input: RequestInfo, init?: RequestInit) {
  const response = await fetch(input, init);
 
  if (!response.ok) {
    throw new Error(response.statusText);
  }
 
  return response.json();
}
 
export default function useRest<T>(
  fn: (fetch: typeof fetchJson) => Promise<T>,
  inputs: Array<unknown>
) {
  const [result, abort] = useAbortablePromise(async signal => {
    try {
      const fetchWithSignal: typeof fetchJson = (input, init) =>
        fetchJson(input, {
          ...init,
          signal
        });
 
      const { start, clear } = timeout(15000);
      return await Promise.race([
        start()
        fn(fetchWithSignal)
      ]).finally(clear);
    } catch (error) {
      if (error.message === 'Timeout') {
        abort();
      }
 
      throw error;
    }
  }, inputs);
  return result;
}

Use it in your components:

function UserList() {
  const [refreshCount, setRefreshCount] = useState(0);
  const { data, error, loading } = useRest(
    fetch =>
      Promise.all([
        fetch('/users/inactive'),
        fetch('/users/active'),
        Promise.resolve(Math.random())
      ]),
    [refreshCount]
  );
 
  return (
    <>
      <button onClick={() => setRefreshCount(c => c + 1)}>Refresh</button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </>
  );
}

License

MIT

Install

npm i use-abortable-promise

DownloadsWeekly Downloads

7

Version

1.1.2

License

MIT

Unpacked Size

9.59 kB

Total Files

10

Last publish

Collaborators

  • avatar