# useLoading()

Helps track loading and error state of imperative async functions.

> **Tip**
>
> [useSuspense()](https://dataclient.io/docs/api/useSuspense.md) or [useDLE()](https://dataclient.io/docs/api/useDLE.md) are better for GET/read endpoints.

## Usage

Files with a `route` are pages mounted at that path, with its params as props; `Link` and `useNavigate()` stand for your router (with react-router, read params with `useParams()`).

```ts title="PostResource"
import { Entity, resource } from '@data-client/rest';

export class Post extends Entity {
  id = 0;
  author = 0;
  title = '';
  body = '';
  votes = 0;

  static key = 'Post';

  get img() {
    return `https://picsum.photos/seed/${this.id % 16}/96/72`;
  }
}
export const PostResource = resource({
  path: '/posts/:id',
  schema: Post,
});
```

```tsx title="PostDetail" route="/posts/:id"
import { useSuspense } from '@data-client/react';
import { PostResource } from './PostResource';

export default function PostDetail({ id }) {
  const post = useSuspense(PostResource.get, { id });
  return (
    <div>
      <div className="voteBlock">
        <img src={post.img} width="70" height="52" />
      </div>
      <div>
        <h4>{post.title}</h4>
        <p>{post.body}</p>
      </div>
    </div>
  );
}
```

```tsx title="PostForm"
export default function PostForm({ onSubmit, loading, error }) {
  const handleSubmit = e => {
    e.preventDefault();
    const data = new FormData(e.target);
    onSubmit(data);
  };
  return (
    <form onSubmit={handleSubmit} aria-busy={loading}>
      <fieldset disabled={loading}>
        <TextInput
          label="Title"
          name="title"
          defaultValue="My New Post"
          required
        />
        <TextArea name="body" rows={12} label="Body" required>
          After clicking 'save', the form is locked and the button animates
          until the POST completes. Upon completion the newly
          created post is displayed immediately as Reactive Data Client is
          able to use the fetch response to populate the store.
        </TextArea>
        {error ? (
          <div className="alert alert--danger">{error.message}</div>
        ) : null}
        <button type="submit">{loading ? 'Saving…' : 'Save'}</button>
      </fieldset>
    </form>
  );
}
```

```tsx title="PostCreate" route="/" {8}
import { useLoading, useController } from '@data-client/react';
import { PostResource } from './PostResource';
import PostForm from './PostForm';

export default function PostCreate() {
  const ctrl = useController();
  const navigate = useNavigate();
  const [handleSubmit, loading, error] = useLoading(
    async data => {
      const post = await ctrl.fetch(PostResource.getList.push, data);
      navigate(`/posts/${post.id}`);
    },
    [ctrl, navigate],
  );
  return (
    <PostForm onSubmit={handleSubmit} loading={loading} error={error} />
  );
}
```

Like [useCallback](https://react.dev/reference/react/useCallback), takes a dependency list to
ensure referential consistency of the function.

## Eslint

> **Tip: Eslint configuration**
>
> Since we use the deps list, be sure to add useLoading to the 'additionalHooks' configuration
> of [react-hooks/exhaustive-deps](https://www.npmjs.com/package/eslint-plugin-react-hooks) rule if you use it.
>
> ```js
> {
>   "rules": {
>     // ...
>     "react-hooks/exhaustive-deps": ["warn", {
>       "additionalHooks": "(useLoading)"
>     }]
>   }
> }
> ```

## Types

```typescript
export default function useLoading<
  F extends (...args: any) => Promise<any>,
>(func: F, deps: readonly any[] = []): [F, boolean];
```

## Examples

### Github pagination

Example app: [github-app](https://github.com/reactive/data-client/tree/master/examples/github-app) ([`src/resources/Issue.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/resources/Issue.tsx), [`src/pages/IssueList.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/pages/IssueList.tsx), [`src/pages/NextPage.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/pages/NextPage.tsx))

### Github comment form submission

Example app: [github-app](https://github.com/reactive/data-client/tree/master/examples/github-app) ([`src/pages/IssueDetail/CreateComment.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/pages/IssueDetail/CreateComment.tsx), [`src/pages/IssueDetail/CommentForm.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/pages/IssueDetail/CommentForm.tsx))
