Search code examples
typescriptvue.jsvue-routeranchorvuejs3

Stop Vue page from loading till data (fetch) is loaded


I am trying to figure out how I can stop my page from loading until my fetch task is done. This is because I am encountering a few issues:

  • I can't re-use the same data, so I have to re-fetch it every time. (Changing views after it is loaded works, but reloading a route breaks)
  • Anchor links don't work, if I have the anchor link #about below some featured items, the #about section will be moved lower because the featured items just finished loading. I can't really use a skeleton loading system, because the amount of items is dynamic.

This is the current code I am working with:

  data() {
    return {
      collections: [],
      items: []
    }
  },
  methods: {
    async fetchCollections() {
      const res = await fetch('http://localhost:4000/collections')
      return await res.json()
    },
    async fetchItems() {
      const res = await fetch('http://localhost:4000/items')
      return await res.json()
    }
  },
  async created() {
    this.collections = await this.fetchCollections()
    this.items = await this.fetchItems()
  }

For all, I know this could be completely bad practice and I am doing everything wrong. Please don't shame me, I am new to Vue and still a bit rough with JavaScript.

To summarize

The intended behavior is for the page to finish loading after the data has been pulled from the backend.

The current behavior means my site will load, and then after a millisecond delay, the content will pop up from the finished fetch.


Solution

  • If your route depends on some data to be fetched, the correct way of doing this is through Vue-router's Navigation guards. Here's docs about this problem.
    Here's some code for your use case:

    async function fetchData() {
      const resCol = await fetch('http://localhost:4000/collections');
      const resColJson = await resCol.json();
      const resItems = await fetch('http://localhost:4000/items');
      const resItemsJson = await resItems.json();
      return { resColJson, resItemsJson };
    }
    export default {
      data() {
        return {
          collections: [],
          items: []
        }
      },
      beforeRouteEnter(to, from, next) {
        fetchData().then(({ resColJson, resItemsJson } ) => {
          next((vm) => {
            vm.collections = resColJson;
            vm.items = resItemsJson;
          });
        });
      },
      beforeRouteUpdate() {
        fetchData().then(({ resColJson, resItemsJson } ) => {
          this.collections = resColJson;
          this.items = resItemsJson;
        });
      },
    }