JAVASCRIPT
Building a Reusable Data Fetching Composable in Vue 3
Create a powerful and reusable custom composable in Vue 3 Composition API to handle data fetching, loading states, and error management efficiently across components.
// src/composables/useFetch.js
import { ref, watchEffect } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(true)
async function doFetch() {
loading.value = true
data.value = null
error.value = null
try {
const res = await fetch(url)
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`)
}
data.value = await res.json()
} catch (e) {
error.value = e
} finally {
loading.value = false
}
}
watchEffect(() => {
doFetch()
})
return { data, error, loading, refetch: doFetch }
}
// src/App.vue
<script setup>
import { useFetch } from './composables/useFetch'
const { data, error, loading, refetch } = useFetch('https://jsonplaceholder.typicode.com/todos/1')
</script>
<template>
<div>
<h1>Todo Item</h1>
<p v-if="loading">Loading...</p>
<p v-else-if="error">Error: {{ error.message }}</p>
<div v-else>
<p>ID: {{ data.id }}</p>
<p>Title: {{ data.title }}</p>
<p>Completed: {{ data.completed ? 'Yes' : 'No' }}</p>
</div>
<button @click="refetch">Refetch Data</button>
</div>
</template>
How it works: This snippet illustrates how to create a custom composable `useFetch` to centralize data fetching logic. The `useFetch` function takes a URL, performs an asynchronous fetch request, and manages `data`, `error`, and `loading` reactive states. It uses `watchEffect` to automatically re-run the fetch when `url` changes (though not shown here, `url` could also be a ref). The `doFetch` function can also be explicitly called via `refetch`. This composable provides a clean, reusable interface for any component needing to fetch data, reducing duplication and improving maintainability. Components simply destructure the returned reactive properties to display fetched data or handle states.