Как я могу переназначить ссылку Vue на другую ссылку и сохранить реактивность?

Мой компонент Vue выглядит так (упрощенно):

<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>

Метод «backendApi.getEvents» выглядит так:

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

Проблема в том, что при начальной загрузке страницы загрузка правильно отображается на веб-сайте, сначала "true", а затем "false" после завершения запроса. Но когда функция «loadEvents» снова запускается при нажатии на div, значение «загрузка» просто остается «ложным» и не обновляется в DOM. Что я делаю неправильно и как я могу это исправить? Я пытался сделать loading.value = res.loading.value, но это только меняет «загрузка» на «истина», а после выполнения запроса не меняется на «ложь».

FWIW, вы, похоже, воспроизводите функциональность Suspense, обрабатывая статус загрузки вручную.

Estus Flask 25.05.2023 22:06
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Краткий ответ будет таким:

Вы не можете переназначить реактивный объект, поскольку старые ссылки будут указывать на предыдущее значение объекта, и реактивность будет потеряна. Все реактивные переменные должны быть объявлены с помощью const.

Но, кажется, вы слишком усложнили логику. Насколько я понимаю, loading должно быть истинным, когда axios загружает данные (просто сделайте правильный импорт):

<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);
  // use results here
  // ...
  loading.value = false;
}

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

</script>

Но вы можете улучшить это и повторно использовать loading, переместив его в бэкэнд API:

// your backend-api.js:

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;
  }
};

// your component
<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);
  // use results here
  // ...
}

</script>

ОБНОВЛЕНИЕ
После некоторого обсуждения с автором появилось новое решение.
Автор указал, что для каждого бэкэнд-вызова требуется ссылка загрузки. Перенести загрузку в бэкэнд API не так уж и хорошо, нам нужна некоторая обертка вызовов API и так далее. Вся логика становится НЕ агностической, что выглядит не идеально. Итак...
Недавно я стал поклонником сеттера с некоторыми действиями, и сеттеры обещаний — мой новый фаворит. Обычно у меня есть какой-то модуль с моими утилитами Vue, поэтому в этом случае я создал promiseRef(), который возвращает логическую ссылку с дополнительным свойством «обещание», которое контролирует его значение false/true. Таким образом, у нас есть загрузка ссылок, независимая от какой-либо другой логики, включая некоторый внутренний API как ненужную зависимость. Итак, вся установка выглядит так:

<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());
    // use your results
  }

</script>

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

бэкэнд-api.js:

// just emulating some 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;
  
};

Работающая суть Vue3 уже здесь!

В вашем втором решении, как я могу иметь переменную «загрузки», специфичную для каждой функции? BackendApi имеет несколько функций, которые можно вызывать одновременно. В этом случае я не смог бы отличить, какой из них все еще загружается, а какой нет с вашим решением.

Lukas 25.05.2023 20:34

@Lukas тогда использует первое решение :)

Alexander Nenashev 25.05.2023 21:02

Что ж, да, это решение работает, но тогда у меня дублируется код везде, где я вызываю метод из backendApi, потому что мне нужно установить загрузку до запроса, а затем после запроса.

Lukas 25.05.2023 21:07

позже приду к решению)

Alexander Nenashev 25.05.2023 21:15

Хорошо, буду ждать, спасибо :)

Lukas 25.05.2023 21:18

@Lukas, ты понял, загляни в раздел ОБНОВЛЕНИЕ :)

Alexander Nenashev 25.05.2023 23:47

@Lukas обновил начало ответа, объяснив, почему переназначение не сработает.

Alexander Nenashev 26.05.2023 14:01

Другие вопросы по теме