# useLoading()

Helps track loading and error state of imperative async functions.

> **Tip**
>
> [useSuspense()](https://dataclient.io/vue/api/useSuspense.md) or [useDLE()](https://dataclient.io/vue/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,
});
```

```html title="PostDetail.vue"
<script setup lang="ts">
  import { computed } from 'vue';
  import { useSuspense } from '@data-client/vue';
  import { PostResource } from './PostResource';

  const props = defineProps<{ id: number }>();
  const post = await useSuspense(PostResource.get, computed(() => ({
    id: props.id,
  })));
</script>

<template>
  <div>
    <div class="voteBlock">
      <img :src="post.img" width="70" height="52" />
    </div>
    <div>
      <h4>{{ post.title }}</h4>
      <p>{{ post.body }}</p>
    </div>
  </div>
</template>
```

```html title="PostForm.vue"
<script setup lang="ts">
  defineProps<{
    loading: boolean;
    error: Error | undefined;
  }>();
  const emit = defineEmits<{ submit: [data: FormData] }>();

  const handleSubmit = (e: Event) => {
    e.preventDefault();
    emit('submit', new FormData(e.target as HTMLFormElement));
  };
</script>

<template>
  <form @submit="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>
    <div v-if="error" class="alert alert--danger">{{ error.message }}</div>
    <div>
      <button type="submit" :disabled="loading">
        {{ loading ? 'saving...' : 'Save' }}
      </button>
    </div>
  </form>
</template>
```

```html title="PostCreate.vue"
<script setup lang="ts">
  import { useLoading, useController } from '@data-client/vue';
  import { PostResource } from './PostResource';
  import PostForm from './PostForm.vue';

  const emit = defineEmits<{ navigateToPost: [id: number] }>();
  const ctrl = useController();
  const [handleSubmit, loading, error] = useLoading(
    async (data: FormData) => {
      const post = await ctrl.fetch(PostResource.getList.push, data);
      emit('navigateToPost', post.id);
    },
  );
</script>

<template>
  <PostForm @submit="handleSubmit" :loading="loading" :error="error" />
</template>
```

```html title="Navigation.vue"
<script setup lang="ts">
  import { ref } from 'vue';
  import PostCreate from './PostCreate.vue';
  import PostDetail from './PostDetail.vue';

  const id = ref<number | undefined>(undefined);
</script>

<template>
  <div v-if="id">
    <Suspense>
      <PostDetail :id="id" />
    </Suspense>
    <div style="text-align: center">
      <button @click="id = undefined">New Post</button>
    </div>
  </div>
  <PostCreate v-else @navigate-to-post="id = $event" />
</template>
```

Returns the wrapped function along with `loading` and `error` [refs](https://vuejs.org/api/reactivity-core.html#ref).
The wrapped function is stable, so no dependency list is needed: any refs or props it reads are
read at call time.

## Types

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

`loading` is `true` while the returned promise is pending. If `func` rejects, the rejection is
caught and stored in `error`; it is cleared again on the next call.

## Examples

### Todo creation

Example app: [vue-todo-app](https://github.com/reactive/data-client/tree/master/examples/vue-todo-app) ([`src/components/TodoList.vue`](https://github.com/reactive/data-client/blob/master/examples/vue-todo-app/src/components/TodoList.vue), [`src/resources/TodoResource.ts`](https://github.com/reactive/data-client/blob/master/examples/vue-todo-app/src/resources/TodoResource.ts))
