# Fetching Data — Vue.js

Source: https://www.skillbyai.com/en/vue/d-fetch

> Loading, error and data states.

## Async composables and query libraries

Every request has at least three states: **loading**, **error** and **success**, and the UI should render all of them. A small composable can hold `data`, `error` and `loading` refs, re-fetch when a reactive input changes (using `watchEffect` with `toValue()` on a ref or getter argument), and ignore stale responses when inputs change quickly. For larger apps, **TanStack Query for Vue** (`@tanstack/vue-query`) adds caching keyed by a `queryKey`, deduplication, background refetching and retries through `useQuery`, plus `useMutation` for writes. Its option and result names have changed between major versions, so check the docs for your version.

## A useFetch composable and its use

Handles all three states.

```javascript
// composables/useFetch.js
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {          // url: string, ref or getter
  const data = ref(null)
  const error = ref(null)
  const loading = ref(false)

  watchEffect(async (onCleanup) => {
    let cancelled = false
    onCleanup(() => { cancelled = true })   // a newer request replaced this one
    loading.value = true
    error.value = null
    try {
      const res = await fetch(toValue(url))
      if (!res.ok) throw new Error(`HTTP ${res.status}`)
      const json = await res.json()
      if (!cancelled) data.value = json
    } catch (e) {
      if (!cancelled) error.value = e
    } finally {
      if (!cancelled) loading.value = false
    }
  })

  return { data, error, loading }
}

// <script setup>
// const props = defineProps({ id: String })
// const { data: user, error, loading } = useFetch(() => `/api/users/${props.id}`)
// </script>
// <template>
//   <p v-if="loading">Loading...</p>
//   <p v-else-if="error">Could not load user: {{ error.message }}</p>
//   <UserCard v-else-if="user" :user="user" />
// </template>
```

## A restaurant order screen

A good order screen shows 'preparing', 'ready' or 'problem with your order', never a blank board. Your UI should be just as clear about loading, success and error.

**Quiz:** Why does the composable call toValue(url) inside watchEffect?

- [ ] To convert the URL to lowercase
- [x] So the effect tracks a ref or getter argument and refetches when it changes
- [ ] To cache the response forever
- [ ] Because fetch only accepts refs

*Answer:* So the effect tracks a ref or getter argument and refetches when it changes. Reading the reactive source inside the effect registers it as a dependency.
