JAVASCRIPT
Reusable Data Fetching Composable with Loading & Error States
Create a robust and reusable data fetching composable in Vue 3 using the Composition API, handling loading, error, and data states efficiently.
import { ref, watchEffect } from 'vue';
export function useFetch(url) {
const data = ref(null);
const error = ref(null);
const loading = ref(true);
watchEffect(async () => {
loading.value = true;
error.value = null;
data.value = null;
try {
const response = await fetch(url.value || url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data.value = await response.json();
} catch (e) {
error.value = e;
} finally {
loading.value = false;
}
});
return { data, error, loading };
}
// How to use in a component:
// <script setup>
// import { ref } from 'vue';
// import { useFetch } from './composables/useFetch';
//
// const postId = ref(1);
// const url = ref(`https://jsonplaceholder.typicode.com/posts/${postId.value}`);
// const { data, error, loading } = useFetch(url);
//
// function fetchNextPost() {
// postId.value++;
// url.value = `https://jsonplaceholder.typicode.com/posts/${postId.value}`;
// }
// </script>
//
// <template>
// <div>
// <button @click="fetchNextPost">Fetch Next Post</button>
// <div v-if="loading">Loading...</div>
// <div v-else-if="error">Error: {{ error.message }}</div>
// <div v-else-if="data">
// <h2>{{ data.title }}</h2>
// <p>{{ data.body }}</p>
// </div>
// </div>
// </template>
How it works: This composable (`useFetch`) encapsulates the logic for fetching data, managing loading and error states. It leverages Vue 3's `ref` for reactive state and `watchEffect` to automatically re-run the fetch operation whenever the URL dependency changes. This pattern promotes code reusability across different components, making data fetching concerns easy to manage and test. The example shows how to use it by dynamically updating the URL to fetch new data.