# 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

```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 `//loremflickr.com/96/72/kitten,cat?lock=${this.id % 16}`;
  }
}
export const PostResource = resource({
  path: '/posts/:id',
  schema: Post,
});
```

```tsx title="PostDetail"
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}>
      <TextInput
        label="Title"
        name="title"
        defaultValue="My New Post"
        required
      />
      <TextArea name="body" rows={12} label="Body" required>
        After clicking 'save', the button will be disabled until the POST
        is completed. 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}
      <div>
        <button type="submit" disabled={loading}>
          {loading ? 'saving...' : 'Save'}
        </button>
      </div>
    </form>
  );
}
```

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

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

```tsx title="Navigation"
import React from 'react';
import PostCreate from './PostCreate';
import PostDetail from './PostDetail';

function Navigation() {
  const [id, setId] = React.useState<undefined | number>(undefined);
  if (id) {
    return (
      <div>
        <PostDetail id={id} />
        <center>
          <button onClick={() => setId(undefined)}>New Post</button>
        </center>
      </div>
    );
  }
  return <PostCreate navigateToPost={setId} />;
}
render(<Navigation />);
```

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))
