uidotdev / usehooks

A collection of modern, server-safe React hooks – from the ui.dev team

Home Page:https://usehooks.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Request: useAsyncStatus - convert an async function into pending/loading/success/error states + a trigger.

dwjohnston opened this issue · comments

The idea is instead of writing a component like:

export function MyComponent(props: {
  doSomethingAsync: () => Promise<'a' | 'b' | 'c'>;
}) {
  const [status, setStatus] = useState<PossibleStates<'a' | 'b' | 'c'>>({
    state: 'pending',
  });

  return (
    <div>
      {status.state === 'loading' && 'Loading...'}
      {status.state === 'error' && 'Error!'}
      <button
        onClick={async () => {
          setStatus({ state: 'loading' });
          try {
            const result = await props.doSomethingAsync();
            setStatus({ state: 'success', data: result });
          } catch (err) {
            setStatus({ state: 'error' });
          }
        }}
      >
        Click me
      </button>
    </div>
  );
}

We would do this:

export function MyComponent(props: {
  doSomethingAsync: () => Promise<'a' | 'b' | 'c'>;
}) {
  const [trigger, status] = useAsyncStatus(props.doSomethingAsync);

  return (
    <div>
      {status.state === 'loading' && 'Loading...'}
      {status.state === 'error' && 'Error!'}
      <button onClick={trigger}>Click me</button>
    </div>
  );
}

Some considerations

  1. Some users would rather use async functions that return errors rather than throw errors.
    eg.
type AsyncFunction<TArgs extends Array<unknown>, TData, TError> = 
    (...args: TArgs) => Promise<{data: TData} | {error: TError}>  
  1. Further gold plating could allow for the hook to also provide for the various feedback rendering, eg:
export function MyComponent(props: {
  doSomethingAsync: () => Promise<'a' | 'b' | 'c'>;
}) {
  const [trigger, status, feedbackJsx] = useAsyncStatus(props.doSomethingAsync, {
      loadingJsx: <span>Loading...</span> 
      errorJsx: <span>Error!</span>
  });

  return (
    <div>
      {feedbackJsx}
      <button onClick={trigger}>Click me</button>
    </div>
  );
}