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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs