A Composable for Loading

Loading data from a file is the same every time: a ref for the data, one for loading, one for an error message, and onMounted with fetch(). Only the file changes. That's a job for a composable, with the file's address as its parameter:

import { onMounted, ref } from "vue";

export function useFetch(url) {
    const data = ref(null);
    const error = ref("");
    const loading = ref(true);

    onMounted(async () => {
        const response = await fetch(url);
        if (!response.ok) {
            error.value = "Sorry, this couldn't be loaded.";
        } else {
            data.value = await response.json();
        }
        loading.value = false;
    });

    return { data, error, loading };
}

A component that needs data calls it with the file's name and gets the three refs:

<script setup>
import { useFetch } from "./useFetch.js";

const { data, error, loading } = useFetch("menu.json");
</script>

What's worth noticing:

  • onMounted works in a composable too, and it belongs to the component that calls the composable: the data is loaded when that component is mounted. Two components that call useFetch() each load their own file, each when it's mounted, and each has its own data, error and loading.
  • data starts as null, which means “nothing yet”: the composable can't know what the file holds, an array, an object or text. The template shows the list only once loading is false, and by then data holds the menu.
  • The error message is a general one, because the composable loads anything.

Composables like this one are in almost every Vue app, and libraries offer ready-made ones. VueUse, a popular collection of composables, has its own useFetch(), and a useLocalStorage() too.

Instructions

  1. Write useFetch(url) in useFetch.js: move the three refs and onMounted from App.vue into it, with menu renamed to data, url in place of "menu.json", and Sorry, this couldn't be loaded. as a general message. Return { data, error, loading }. In App.vue, import it, take the refs out with const { data, error, loading } = useFetch("menu.json");, and show item in data in the list.

  2. Now the reviews: in Reviews.vue, load reviews.json with useFetch(), and show each review like Anna: The best carrot cake in town., with Loading… while they load, as the menu does.