Is it possible to transfer the reactivity of a Vue ref to another ref while reassigning it?

Below is a simplified version of my Vue component:

<template>
  <div @click="loadEvents">{{ loading }}</div>
</template>

<script setup>
import { ref } from 'vue'
let loading = ref(false)

loadEvents()
function loadEvents() {
  const res = backendApi.getEvents(selectedDate.value)
  loading = ref(res.loading)
}
</script>

The function "backendApi.getEvents" is structured like this:

getEvents() {
  const loading = ref(true)
  axios.get(...).then(r => loading.value = false)
  
  return { loading }
}

Upon the initial page load, the value of loading displays correctly as "true" and then changes to "false" after the request completion. However, when the "loadEvents" function is triggered again by clicking on the div, the "loading" value remains "false" and does not update in the DOM. What am I doing incorrectly and how can I resolve this issue? I attempted

loading.value = res.loading.value
, but it only changes "loading" to "true" without transitioning back to "false" once the request finishes.

Answer №1

Here is a brief summary:

You cannot reassign a reactive object because it will break the reactivity and cause the old references to lose their connection to the object's value. Therefore, it is recommended to declare all reactive variables using const.

It appears that the logic you have provided is overly complex. In my understanding, the loading variable should be set to true when axios is in the process of loading data. Make sure that your imports are correct:

<template>
  <div @click="loadEvents">{{ loading }}</div>
</template>

<script setup>

const loading = ref(false);

loadEvents()

async function loadEvents() {
  loading.value = true;
  const results = await backendApi.getEvents(selectedDate.value);
  
  // perform actions with results here
  
  loading.value = false;
}

function getEvents() {
  return axios.get(...);
}

</script>

However, there is room for improvement by refactoring the code and reusing the loading variable within the backend api:

// backend-api.js file:

export const loading = ref(false);

export const backendApi = {
  getEvents(){
    return this.callApi(...);
  },
  async callApi(...args){
    loading.value = true;
    const result = await axios.get(...args);
    loading.value = false;
    return result;
  }
};

// component file
<template>
  <div @click="loadEvents">{{ loading }}</div>
</template>

<script setup>

import {loading, backendApi} from './backend-api.js';

loadEvents()

async function loadEvents() {
  const results = await backendApi.getEvents(selectedDate.value);
  
  // perform actions with results here
  
}

</script>

UPDATE
After some discussion with the original author, a new solution has emerged.
It was highlighted that each backend call requires its own loading reference. Moving the loading variable into the backend api might not be ideal as it can introduce dependency coupling. One recommended approach is to use setters with promise-based actions. For instance, creating a utility function like promiseRef() can help manage loading states independently. Here is how the modified setup looks:

<script setup>
    
  import { backendApi } from './backend-api.js';
  import { promiseRef } from './utils.js'
  
  const loading = promiseRef();

  async function loadEvents(){
     const result = await (loading.promise = backendApi.getEvents());
     
    // utilize the fetched results
     
  }

</script>

<template>
  <button @click="loadEvents">{{ loading }}</button>
</template>

backend-api.js:

// This is a simulated API call
export const backendApi = {
  getEvents(){
    return new Promise(resolve => setTimeout(resolve, 2000));
  }  
}

utils.js:

import { ref } from 'vue';
export const promiseRef = value => {
  
  const loading = ref(value || false);
  let promise;
  
  Object.defineProperty(loading, 'promise', {
    get(){
      return promise;
    },
    set(val){
      loading.value = true;
      (promise = val).then(() => loading.value = false);
    }
  });
  
  return loading;
  
};

View a working example on Vue3 gist!

Answer №2

Looking for a solution? Check out Composables. There is a helpful example in the documentation that demonstrates how to use fetch.

To create your own composable, follow the example and maintain reactivity by utilizing reactive(useComposable) and computed:

import { computed, reactive } from 'vue';
import { useLoadEvents } from './loadEvents.js';

const events = reactive(useLoadEvents());
const loading = computed(() => events.loading);

You can also skip using computed and directly reference events.loading in the template.

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

Learn how to effectively share an image using the Math.random function

Is there a way to display a random number of images on a webpage using JavaScript? Let's say we want to show X number of images, where X is a randomly generated number. For the sake of this example, let's set X to be 10. <input class="randomb ...

Using knex.js to pipe data to an express server

I'm encountering an issue with knex.js and express. Here is the code snippet in question: userRouter.get('/:userId', function (req, res) { DB('users').where({ id: req.params.userId }).first('name').pipe(res); }); ...

increasing the size of the input thumb compared to the others

Hello fellow React developers, I'm currently learning by coding and I have a question about a slider. I'm trying to make the thumb of the slider bigger, but when I increase its size it doesn't fully display within the input area (as you can ...

Obtain the jQuery dialog's closure event within the $.Ajax completion function

I have developed a custom jQuery plugin that utilizes jQuery Dialog to showcase messages. Specifically, I am using it within my jQuery $.ajax -->done function. My goal is to capture the close event of the Dialog in the .ajax function so that I can redire ...

Resolving issues with setting up d3.js in the 'Creating a Custom Map' guide

Starting Mike Bostock's tutorial on creating a map, but facing some installation issues at the beginning. I am using Windows 8.1 for this. This is the specific part that's causing trouble: "To get started, you'll need the reference implemen ...

Guide on parsing a JavaScript file and converting the default export module to JSON with Node.js

What I'm trying to accomplish in my Node.js project is reading a sample.js file with ES Module syntax and extracting the default export from it. sample.js import foo from "foo"; const bar = [ { name: "Homer", }, { n ...

Using jQuery, identify when any of the elements within every function are missing

In my JavaScript file, I have the following code snippet: var aryYears= []; $(".year").each(function(){ aryYears.push($(this).val()); }) This allows me to pass an array of years as a parameter in the saveChanges function. I want to make ...

How to retrieve the name of a node using partial text in JavaScript

I am looking for a way to separate values into key-value pairs before and after a specified delimiter (:) in JavaScript. For example: Product Name: Product 15 Product code: 1234 If I search for "product," I want to retrieve the product name and product c ...

Exploring the MEVN Stack's Integration with Image Uploading

After successfully implementing an image upload system on my website, I encountered difficulty in linking to the uploaded images. The root directory of my project includes a client folder for vuejs app and a server folder for backend logic. When users upl ...

in javascript, how can you access the value of a parent object within a nested object?

Within the material ui framework, I am utilizing the createMuiTheme function to create a theme. How can I access the values of the parent object within a nested object? (I am aware that I can declare global variables above the function); To better unders ...

Navigating the world of updating problems with Express.js

Currently, I am working on a MEAN stack project and facing challenges with the routing for updating. Saving operations are functioning correctly. In my Angular controller, I have defined the following scope method in which staff represents the object subm ...

Hidden content from Vue router view

My goal is to have the navigation pane overlaying the content of the page, but instead, it is being appended to the bottom where the end of the navigation drawer would be. I've experimented with different variations to display data using navigation dr ...

Unique Javascript Library Focused on AJAX

Looking for a specific JavaScript library that focuses solely on AJAX functionality, such as a basic XMLHttp wrapper. ...

Converting a PHP timestamp to a jQuery-compatible format

Can someone help me find the equivalent function in jQuery that will give me a time format similar to this: date( 'Y-m-d\TH:i:sP'); //the output is like this. 2013-10-30T18:10:28+01:00 I am looking for this specific format in jQuery to use ...

Utilizing JavaScript For Loops for Code Repetition

Apologies for the ambiguous question title - struggling to articulate this properly. Essentially, I have some JavaScript code that I am looking to streamline by using a for loop. $('.q1').keyup(function () { if ($.inArray($(this).val().toLo ...

Using both Promise based architecture and events in Node.js can lead to unexpected behavior and should be avoided

Currently, I am developing a nodejs application that is expected to grow in size. Despite my efforts, I have not been able to find many resources on advanced Nodejs project architecture and structure. I am wondering if it would be considered bad practice ...

I'm currently utilizing lint in my Angular 2+ project. Is there a way to arrange the constructor parameters in alphabetical order

I am struggling to organize the constructor parameters in TypeScript while using TSLINT with Angular9. I am looking for a rule similar to member-ordering that can help me sort them effectively. constructor( // Sort these private readonly router: R ...

Using Node.js to execute JavaScript with imported modules via the command line

Having limited experience with running JavaScript from the command line, I am facing a situation where I need to utilize an NPM package for controlling a Panasonic AC unit, which includes a wrapper for their unofficial API. My objective is to create a sim ...

Issue with firing Facebook pixel after router.push() in Next.js

Within this code block is FB pixel tracking code <Script id="some-id" strategy="afterInteractive">some fb pixel code</Script> The issue arises when navigating to a page containing the script using router.push(SOME_ROUTE). T ...

Using anti-cache code in jQuery's getJson function

During an interview, I was presented with the following question: Can you provide a one-liner code showing the proper syntax to add an anti-cache code to all jQuery getJson calls in a project without individually adding it to each call? I must admit th ...