JAVASCRIPT
Handling Asynchronous Data Fetching in Vue 3 Composables
Learn to manage API calls and asynchronous data fetching within Vue 3 composables, ensuring clean setup and cleanup for robust applications.
// composables/useFetch.js
import { ref, onMounted, onUnmounted, watch } from 'vue';
export function useFetch(url) {
const data = ref(null);
const error = ref(null);
const loading = ref(true);
let abortController = null;
const fetchData = async (currentUrl) => {
loading.value = true;
error.value = null;
data.value = null;
if (abortController) {
abortController.abort(); // Abort previous request if ongoing
}
abortController = new AbortController();
const signal = abortController.signal;
try {
const response = await fetch(currentUrl, { signal });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data.value = await response.json();
} catch (e) {
if (e.name !== 'AbortError') { // Ignore abort errors
error.value = e;
console.error('Fetch error:', e);
}
} finally {
loading.value = false;
}
};
onMounted(() => {
watch(url, (newUrl) => {
if (newUrl) fetchData(newUrl);
}, { immediate: true }); // Fetch immediately on mount
});
onUnmounted(() => {
if (abortController) {
abortController.abort(); // Clean up on component unmount
}
});
return { data, error, loading };
}
// MyComponent.vue
<template>
<div>
<h2>Data Fetching Example</h2>
<p>Current Post ID: {{ postId }}</p>
<button @click="postId++">Next Post</button>
<button @click="postId = 1">Reset Post ID</button>
<div v-if="loading">Loading data...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<div v-else-if="data">
<h3>{{ data.title }}</h3>
<p>{{ data.body }}</p>
</div>
<div v-else>No data loaded.</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useFetch } from '../composables/useFetch';
const postId = ref(1);
const postUrl = computed(() => `https://jsonplaceholder.typicode.com/posts/${postId.value}`);
const { data, error, loading } = useFetch(postUrl);
</script>
<style scoped>
button {
margin-right: 10px;
padding: 8px 15px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
div {
margin-top: 15px;
}
</style>
How it works: This snippet demonstrates creating a `useFetch` composable in Vue 3 to manage asynchronous data fetching. It uses `ref` for reactive `data`, `error`, and `loading` states. `onMounted` is used to initiate the data fetch immediately and to set up a `watch` effect that re-fetches data whenever the provided `url` changes, making it reactive. `onUnmounted` ensures proper cleanup by aborting any pending fetch requests using `AbortController`, preventing memory leaks and unnecessary network activity when the component unmounts. This pattern provides a robust and reusable solution for handling API calls within Vue applications.