Using the Vue JS Composition API to call a composable async function in both the setup and an event function

Just to clarify, I want to mention that I am relatively new to working with Vue. Currently, I am in the process of developing a data visualization project to familiarize myself with the various concepts involved.

In order to fetch data from an API, I have implemented the following function as a composable:

import {ref} from 'vue'
const getData = (endpoint) => {
    const data = ref([])
    const error = ref(null)
    const load = async() => {
      try{
        let response = await fetch("BASE_URL"+endpoint)
        if (!response.ok){
          throw Error('Error when trying to fetch data')
        }
        data.value = await response.json()
      }
      catch (err){
        error.value = err.message
      }
    }
    return{data, error, load}
}
export default getData

Currently, I am fetching the data within the setup function and displaying it on the template. However, I require assistance in making another API call after a specific event, such as an @click event, in order to update the data displayed on the page.

The code snippet below illustrates my current approach, where the handleClick function is not functioning as intended. The desired outcome is for the template to display data from endpoint2 upon clicking the button. It is possible that I have misunderstood some key concepts, which is why I am reaching out to the community for guidance. Your help would be greatly appreciated.

<template>
    <div>
      <button @click="handleClick">click me</button>
    </div>
    <div>{{data}}</div>
</template>

<script>
import {ref} from 'vue'
import getData from '../composables/getData'

export default {
  name: 'HomeView',
  setup(){
    const {data, error, load} = getData('endpoint1')
    load()

    const handleClick = () => {
            console.log('clicked')
            const {data, error, load} = getData('endpoint2')
            load()
        }
    return {data, handleClick}
  },
}
</script>

Answer №1

getData essentially functions as a composition function (typically named with a prefix like useData to indicate their purpose), although its usage in this case is incorrect.

Composition functions should be directly called within the setup function or block. Any other form of usage would depend on the specific implementation and require additional confirmation. The fact that the function returns refs suggests that these refs are intended to be created once and then accessed for their values.

It's generally not recommended to include asynchronous side effects such as load within the setup. These actions typically occur during component mount, where lifecycle hooks and event listeners can handle any errors from promises. In this scenario, a rejection from load() could potentially go unhandled, though there may be fail-safe measures in place using catch.

Recommended approach:

const {data: data1, error: error1, load: load1} = useData('endpoint1')
const {data: data2, error: error2, load: load2} = useData('endpoint2')

onMounted(() => {
  return load1()
})

const handleClick = () => {
  return load2()
}

return {data1,...}

Answer №2

The problem lies right here.

const {data, error, load} = fetchData('endpoint2')

This code snippet does not actually update the data retrieved by the setup function; instead, it simply assigns variables named (data, error, load) without utilizing them.

I suggest converting your composable function to an asynchronous one rather than relying on the load function.

Additionally, initialize a ref with the initial value using the getData function.

In the handle click event, make sure to reset the ref.

Answer №3

One possible solution is to attempt overriding the reference.

const handleClick = () => {
        console.log('clicked')
        const {data: scopedData, error, load} = fetchData('endpoint2')
        data = scopedData();
        load()
}

Answer №4

After receiving valuable feedback, I successfully resolved the issue that had been troubling me. Huge thanks to everyone who contributed!

Following @Estus Flask's advice, I made adjustments to my composable by relocating the argument from the getData reference to the load function itself. Here is the updated code:

const retrieveData = () => {
    const data = ref([])
    const error = ref(null)
    const load = async(endpoint) => {
      try{
        let response = await fetch("BASE_URL"+endpoint)
        if (!response.ok){
          throw Error('Error when trying to fetch data')
        }
        data.value = await response.json()
      }
      catch (err){
        error.value = err.message
      }
    }
    return{data, error, load}
}
export default retrieveData

The setup and handleClick functions are now structured as follows:

setup(){
    const endpoint = ref('endpoint1')
    const {data, error, load} = retrieveData()
    load(endpoint.value)

    const handleClick = () => {
            endpoint.value = 'endpoint2'            
            load(endpoint.value)
        }
    return {data, handleClick}

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

Ways to distinguish if Dynamics XRM javascript is being triggered from the unified interface (UCI) or the traditional web-client interface

Is there an official way to determine if code is being called from the UCI or the legacy web-client? I see the function Xrm.Internal.isUci(), but since it's labeled as Internal, it may not be recommended for use. However, I still need a method to diff ...

The show.bs.modal event has been triggered when the .show() method is used on elements within the

Recently, I discovered that the event show.bs.modal is triggered not only when the modal itself is shown, but also every time you call the .show() method for an element within the modal. To attach the event handler, you would typically use the following c ...

The integration of Google Translate with Javascript on HtmlEditorExtender is experiencing difficulties and is not functioning properly

I implemented the use of a text box with ajaxtoolkit HtmlEditorExtender (Rich textbox) to translate English to Gujarati using Google translation Javascript. The translation function works perfectly with the regular text box, but encounters issues when used ...

Sending Massive Real-Time Analytical Data

I am currently in the process of developing a dynamic analytics website using Asp.Net and C#. I am seeking advice on the most effective way to transmit data from the server to the client. Here are some options I have considered so far: Utilizing an Asp ...

ACE.js enhances website security through Content Security Policy

I've been working on setting up a helmet-csp with ace running smoothly. Here's how my helmet-setup looks: var csp = require("helmet-csp"); app.use(csp({ directives: { defaultSrc: ["'self'", "https://localhost:8000"], ...

Is there a way to show output on render rather than using console.log in node.js?

I have successfully sorted the objects as shown in the link below. My next challenge is to integrate the sorted object into my render function rather than just using console.log(). I'm uncertain if converting it back into an object is the right appro ...

Fetching promise from API and storing it in global variable

Apologies if this is basic, but I'm new to Vue and not very strong in JavaScript..... I am attempting to fetch data from an API and then store that data in a global object so that it can be passed down to child components. I am facing issues with se ...

AngularJS seems to be failing to display the initial option in the input select field

When using Angularjs, binding an input select to the model results in a new empty option being created <option value="? undefined:undefined ?"></option> Here is an example of the code: <select name="category" ng-model="hotspot.category"&g ...

Transmitting a JSON string to my backend system to insert into my database, but unfortunately, no data is being added

I've been facing a challenging issue with my code Currently, I am attempting to insert an object into my database using jQuery/AJAX. Despite not encountering any errors, the data is not getting added to my DB. Here is the snippet of my JS/JQuery cod ...

AngularJS offers a function known as DataSource for managing data sources

During a recent project, I had to convert xml data to json and parse it for my app. One issue I encountered was related to the DataSource.get() function callback in the controller. After converting the xml data using a service, I stored the converted data ...

Guide to developing a universal store extension

I've been attempting to create a reactive global $store object using a plugin, but so far I have not been successful in getting it to function as intended. store.ts: import {reactive} from "vue"; export default { install: (app:any, opt ...

Is there a way to retrieve consistent parent results even when querying children in different orders? (One to Many)

I'm currently developing a chat application that supports private rooms. My main goal is to identify and locate a room that two users are part of. If such a room doesn't exist, I need to create one. Chat Schema export const ChatSchema = new mong ...

The success of the second API call relies on the response generated by the first API call

To start, I need to retrieve coordinates from the geolocation API and then use those coordinates to fetch weather conditions. Here's how I'll do it: const { data: cords } = useQuery("cords", () => { navigator.geolocation.getC ...

The resolvers contain the Query.Mutation but it is not specified in the schema

const { ApolloServer, gql } = require('apollo-server-express'); const express = require('express'); const port = process.env.PORT || 4000; const notes = [ { id: '1', content: 'This is a note', author: 'Adam ...

Each time the Tooltipster content is opened, it seems to be duplicating itself

I'm currently utilizing Tooltipster to display a list of clickable items that users can insert into a textarea. Upon creating a tooltip, I extract its item list by using selectors = $("ul.alternates > li"); However, I'm facing an issue where ...

Capturing information from a modifiable table using Javascript

Creating an HTML Editable table with the property CONTENTEDITABLE, each TD has a unique ID (even though it's a long process and I only have basic JS knowledge) to keep track of the information inside. The table is wrapped by a form. At the end of the ...

jQuery shorthand conditional statements

I currently have a jQuery function that is triggered by two separate buttons. $("#btnSearch, #btnDirectorSearch").click(function () { The construction of the HTML output within this function relies on which button was clicked. To accomplish this, I am ut ...

What distinguishes reference types from primitive types like boolean and numbers?

Is it true that we have the option to define numbers like this: const num = 10; Or can we only use this way of declaration: let num = new Number(10); I'm curious if I am limited to the first variation for declaring numbers. ...

How can I delay the loading of a link until the pop-up is closed?

I have successfully implemented a pop-up on my website, but I am facing an issue where I need to prevent any linked pages from loading until the visitor clicks on the accept button. However, I am struggling to make it function as intended. Below is the sn ...

Guide on implementing a globalThis polyfill within a NextJS application

Having some trouble with the Mantine react component library on older iOS versions. It's throwing a ReferenceError: Can't find variable: globalThis I've looked into polyfills, but I'm struggling to figure out how to integrate it into ...