JAVASCRIPT
Creating a Reusable Data Fetching Composable
Learn how to build a custom Vue 3 Composable to encapsulate asynchronous data fetching logic, providing reactivity, loading states, and error handling for cleaner components.
// composables/useFetch.js
import { ref, onMounted, isRef, unref, watch } from 'vue';
export function useFetch(url) {
const data = ref(null);
const error = ref(null);
const loading = ref(true);
async function doFetch() {
// reset state before fetching
data.value = null;
error.value = null;
loading.value = true;
try {
const res = await fetch(unref(url));
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
data.value = await res.json();
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
}
if (isRef(url)) {
watch(url, doFetch, { immediate: true });
} else {
onMounted(doFetch);
}
return { data, error, loading, doFetch };
}
// Usage in a Vue Component:
// <template>
// <div v-if="loading">Loading...</div>
// <div v-else-if="error">Error: {{ error.message }}</div>
// <div v-else>
// <h1>{{ data.title }}</h1>
// <p>{{ data.body }}</p>
// </div>
// </template>
// <script setup>
// import { useFetch } from './composables/useFetch';
// import { ref } from 'vue';
// const postId = ref(1); // Example with reactive URL
// const { data, error, loading } = useFetch(() => `https://jsonplaceholder.typicode.com/posts/${postId.value}`);
// // Or static URL:
// // const { data, error, loading } = useFetch('https://jsonplaceholder.typicode.com/todos/1');
// // Example to refetch
// // function fetchNextPost() {
// // postId.value++;
// // }
// </script>
How it works: This snippet demonstrates a custom `useFetch` Composable for Vue 3. It encapsulates the logic for making an asynchronous API call, managing loading and error states, and exposing them reactively. It supports both static and reactive URLs, automatically refetching when a reactive URL changes, making data fetching reusable across components.