Best practices for managing data loading with composition API and onBeforeRouteUpdate

I have a Vue 3 single-page component that contains the following script:

export default defineComponent({
  props: {
    id: String,
  },
  setup(props) {
    const error = ref<boolean>(false)
    const thisCategory = ref<CategoryDetails>()
    const subcategories= ref<Category[]>()

    const fetchData = (categoryId: string) => {
      fetchCategoryDetails(categoryId)
        .then((v) => {
          thisCategory.value = v
          subcategories.value = v.child_categories
        })
        .catch(() => error.value = true)
    }

    fetchData(props.id || "")

    onBeforeRouteUpdate((to) => fetchData(to.params.id as string))

    return {
      error,
      thisCategory,
      subcategories,
    }
  }
})

The function fetchData is used to retrieve necessary data for the view. It is called twice within setup(): once to load data when the component is initialized, and once again in onBeforeRouteUpdate to ensure new data is loaded when navigating between links of the same component with different data.

I foresee needing to replicate this behavior in other components and am struggling to abstract it successfully.

An additional challenge arises when getting the ID from props directly versus using to.params in the route guard to ensure correct loading of new IDs.

How can I create a single function to handle both initial data loading and navigation changes, which can be easily implemented across various components by simply passing in a function like fetchData?

Answer №1

Revised Version

If you aim to streamline the process by handling error and incorporating the onBeforeRouteUpdate listener exclusively, managing variable updates through the parent is still necessary. Therefore, you must manage the updates of local variables like thisCategory and subcategories using a method that you provide.

import { ref, computed } from "vue";
import { onBeforeRouteUpdate } from "vue-router";

export const STATE = {
  INIT: "INIT",
  LOADING: "LOADING",
  SUCCESS: "SUCCESS",
  ERROR: "ERROR",
};

export const withFetchResource = (fetchMethod, id) => {
  const loadingState = ref(STATE.INIT);
  const error = computed(() => loadingState.value === STATE.ERROR);

  const fetchData = (resourceId) => {
    loadingState.value = STATE.LOADING;
    fetchMethod(resourceId)
      .then((response) => {
        loadingState.value = LOADING.SUCCESS;
        return response;
      })
      .catch((error) => {
        loadingState.value = LOADING.ERROR;
      });
  };

  fetchData(id.value);

  onBeforeRouteUpdate((to) => fetchData(to.params.id as string));

  return { error, loadingState, fetchData };
};
import { ref, defineComponent } from "vue";
import { STATE, fetchCategoryDetails } from "./myApi.ts";
import { withFetchResource } from "./withFetchResource.ts";

export default defineComponent({
  props: {
    id: String,
  },
  setup(props) {
    const thisCategory = ref<CategoryDetails>();
    const subcategories = ref<Category[]>();

    const categoryFetchHandler = (id) => {
      return fetchCategoryDetails(id).then((v) => {
        thisCategory.value = v;
        subcategories.value = v.child_categories;
      });
    };

    const { error: categoryError } = withFetchResource(
      categoryFetchHandler,
      props.id || ""
    );

    return {};
  },
});

Perhaps consider moving the entire content to another file or method.

// withFetchCategoryDetail.ts
import {ref} from "vue";
import {onBeforeRouteUpdate} from "vue-router";
import {fetchCategoryDetails} from "./myApi.ts";

export const withFetchCategoryDetail = (props) => {
  const error = ref<boolean>(false)
  const thisCategory = ref<CategoryDetails>()
  const subcategories= ref<Category[]>()

  const fetchData = (categoryId: string) => {
    fetchCategoryDetails(categoryId)
      .then((v) => {
        thisCategory.value = v
        subcategories.value = v.child_categories
      })
      .catch(() => error.value = true)
  }

  fetchData(props.id || "")

  onBeforeRouteUpdate((to) => fetchData(to.params.id as string))

  return {
    error,
    thisCategory,
    subcategories,
  }
}
import { ref, onBeforeRouteUpdate, defineComponent } from "vue";
import { withFetchCategoryDetail } from "./withFetchCategoryDetail.ts";

export default defineComponent({
  props: {
    id: String,
  },
  setup(props) {
    return {
      ...withFetchCategoryDetail(props)
    };
  },
});

I haven't tested whether onBeforeRouteUpdate will work within the hook method, but can't see why it wouldn't

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

On a mobile device, the keyboard is hiding the PrimeNG dropdown

While my dropdown works flawlessly on a desktop browser, I encountered an issue when accessing it on an Android device. The dropdown immediately disappears and the virtual keyboard pops up, which is not the case on iOS devices. I suspect that the problem ...

Make sure to verify if the mode in Angular is either visible-print or hidden-print

Here is a snippet of code <div class="row"> <div class="col-sm-12 visible-print"> Content (display in full width when printed) </div> <div class="col-sm-6 hidden-print"> Content (same as above but only half width when ...

Enhance the focus on a textarea by adding specific text using VueJS/Vuetify

When working with the Vuetify textarea, I am attempting to insert some text tags from a button click. <v-btn small flat @click.stop="insertTag('{{title}}', <model-name-here>)">+ Title</v-btn> The method insertTag is supposed t ...

Using asynchronous functions in a loop in Node.js

Although this question may have been asked before, I am struggling to understand how things work and that is why I am starting a new thread. con.query(sql,[req.params.quizId],(err,rows,fields)=>{ //rows contains questions if(err) throw err; ...

What is the best way to transform a string array into a number array?

I'm attempting to convert a string array into a number array and after conducting some research online, I came across this solution: let numbersAsStringArray = originalQueryParams[property] ?? [] let numbers = numbersAsStringArray.map((i) => ...

Restrict scrolling within content boundaries

Issue: I'm facing a problem with controlling the scrolling behavior in a typical Vue 3 SPA. I want to restrict the scroll bar to the content area only and prevent it from extending into the header and footer sections. The Header and Footer elements a ...

Having trouble creating a unit test for exporting to CSV in Angular

Attempting to create a unit test case for the export-to-csv library within an Angular project. Encountering an error where generateCsv is not being called. Despite seeing the code executed in the coverage report, the function is not triggered. Below is the ...

Error detected in Deno project's tsconfig.json file, spreading into other project files - yet code executes without issues?

I am working on a Deno project and need to utilize the ES2019 flatMap() method on an array. To do this, I have created a tsconfig.json file with the following configuration: { "compilerOptions": { "target": "es5", ...

The Stencil EventEmitter fails to send data to the Vue instance

Attempting to develop a custom component using Stencil with an input feature. The goal is to create a component with an input field that, upon value change, emits the input to the Vue instance and logs this event to the console (later updating the value in ...

How to utilize string interpolation in Vue 3 template attribute with quotation marks

I'm struggling to make the method buildAbbrToolTip() utilize the dynamic data from column.m_parse_hi in order to display the correct text definition for an abbreviation in the tooltip. Currently, I'm encountering a console error that says TypeEr ...

Leveraging the power of RXJS and typescript for executing work conditionally between Server and Client Code

I am in need of a function that will assess various conditions to determine if an object is eligible for editing. These conditions may exist on both the server and client sides. Certain conditions should halt further validation and return a value. ...

Setting the font size for the entire body of a webpage globally is ineffective

Technology Stack: Nuxt.js + Vuetify.js Problem: Unable to set global body font size Solution Attempt: I tried to adjust the body font size to 40px in ~/assets/style/app.styl: // Import Vuetify styling ...

Exploring Vue 3: Crafting a custom plugin using the composition API and enhancing it with Typescript type augmentation

Encountering an issue with displaying plugins properly within <script> and <template> tags on WebStorm. Firstly, let's take a look at my files and configuration: tsconfig.config.json { "extends": "@vue/tsconfig/tsconfig. ...

Create a loop in Vue.js 3 without the need for a query

Can someone help me understand how to fetch data using a loop in Vue.js version 3 when working with queries? I am trying to retrieve an object based on its id, which is obtained from the URL. However, I seem to be facing some difficulties. Any guidance wou ...

Exploring the power of Node JS with Promise.all and forEach within a nested array

When working on a Node app with typescript, I encountered the need to iterate through an array while calling an asynchronous function inside the loop to fetch information about related items for each item in the array. The function is called for each relat ...

The error message "npm run dev Error: The derived class t must have a greater number of constructor arguments than its base class" is indicating a mismatch in the number of constructor arguments between

When using vue with Laravel 5.8 in Vagrant, I encountered an error while running npm run development as shown below: root@ubuntu-xenial:/vagrant/webroot# npm run development > @ development /vagrant/webroot > cross-env NODE_ENV=development node_modu ...

Express server experiencing issues with generating Swagger documentation

I've been working on an Express API and decided to implement documentation using Swagger and JSDoc. However, the documentation is not working as expected. Here's how I've set it up: docs.ts: import swaggerJSDoc, { Options } from "swagg ...

Interfacing Contact Form Data from Vue Application to Magento Using API - A Step-by-Step Guide

Introduction A custom vue-component has been implemented on the application, serving as a contact form. This component is imported into the header component and enclosed within a modal container. The primary function of this contact form is to trigger an ...

Retrieve the following object in an array of objects using a specific property value in Javascript

I am working with an array of objects structured like this: orders: [ 0: { order_id: 234, text: 'foo' }, 1: { order_id: 567, text: 'bar' } ] Suppose I have the ID 234 and I want to find the next object in the a ...

I am attempting to store the primary array in local storage, but unfortunately, the value is not being saved within the React context API

I attempted to store the main array in local storage and retrieve it as global state, but I am facing an issue where the data is not being saved in the local storage. This file represents my context. import { createContext, useReducer, ReactNode, FC, use ...