The reactivity of Vue3 async composable export is not maintained

I am currently working on a Vue 3 composable that fetches navigation menus asynchronously. However, I am facing an issue where the exported values are not available when importing them in a component. I would like to find a way to be notified once these values are ready.

(I am using this composable with Nuxt 3, so you may not see imports in my code since they are auto-imported).

// composables/useNavigation.js

// creating a global variable for shared state
const navs = reactive({
  main: false,
  footer: false,
})

export const useNavigation = async () => {
  const runTimeConfig = useRuntimeConfig()
  const endpointMenus = `${runTimeConfig.public.API_URL}/wp-api-menus/v2/menus`
  const { data: menus, pending, error } = await useFetch(endpointMenus)

  Object.keys(navs).forEach(async (key) => {
    const menuID = menus.value.find((m) => m.slug === key)?.ID
    const endpointMenu = `${runTimeConfig.public.API_URL}/wp-api-menus/v2/menus/${menuID}`
    const { data: menu } = await useFetch(endpointMenu)
    navs[key] = menu.value

    console.log('navs.value: ', toRaw(navs))
  })

  return {
    ...toRefs(navs),
    test: toRef(navs.main),
  }
}

In my component file:

// /pages/[slug].vue

<script setup>
const { main, test } = useNavigation()

console.log('main1: ', main)
console.log('test1: ', test)

watch(
  main,
  (newValue, oldValue) => {
    console.log('newValue: ', newValue)
  },
  { deep: true, immediate: true },
)

const menu = computed(() => {
  console.log('main: ', main)
  return main
})

setTimeout(() => {
  console.log('main2: ', main)
  console.log('test2: ', test)
}, 3000)

The initial console log displays both main and test as undefined.

During the timeout period, I can observe that the navs object is correctly filled with the results.

After 3 seconds, main and footer should be updated, but upon logging them again, they remain undefined.

Additionally, the watcher does not seem to trigger.

What could I be overlooking? I tried including the toRefs part to maintain reactivity, but it did not resolve the issue.

Answer №1

the toRef value cannot be accessed during initialization due to being unresponsive when initialized with toRef(navs.main). To ensure that useNavigation always returns a Promise, you can either use <script setup async> or exclude the startup state as shown below:

// composables/useNavigation.js

// shared state using a global variable
const navs = reactive({
  main: false,
  footer: false,
})

export const useNavigation = () => {
  const runTimeConfig = useRuntimeConfig()
  const endpointMenus = `${runTimeConfig.public.API_URL}/wp-api-menus/v2/menus`

  async function init() {
    const { data: menus, pending, error } = await useFetch(endpointMenus)

    Object.keys(navs).forEach(async (key) => {
      const menuID = menus.value.find((m) => m.slug === key)?.ID
      const endpointMenu = `${runTimeConfig.public.API_URL}/wp-api-menus/v2/menus/${menuID}`
      const { data: menu } = await useFetch(endpointMenu)
      navs[key] = menu.value

      console.log('navs.value: ', toRaw(navs))
    })
  }

  void init()

  return {
    ...toRefs(navs),
    test: computed(() => navs.main)
  }
}

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

Upgrade from Vuetify 2 to Vuetify 3, new changes including the elimination of v-list-item-content and v-list

I'm currently in the process of upgrading from Vuetify/Vue 2 to Vue 3. As someone who is not primarily a front-end developer, I have been tasked with updating some older code to ensure everything continues to work smoothly. However, I've run into ...

Ways to recover HTML elements that have been eliminated by jQuery's remove

$(document).ready(function() { $('#remove').click(function() { $('.test').remove(); }); }); <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div class="test-wra ...

Determine which children should be displayed in a Kendo treeview based on the field titled "ParentId"

After converting a list of records into JSON format, I am looking to transform this data into a hierarchical KendoTreeView. Here is the JSON data: [ { "id": 1, "name": "A", "parentID": 0, "hasItems": "true" }, { "id": 2, "name": "B", "parentID": 1, "has ...

Implementing a "Click to view additional comments" feature using PHP and AJAX

I've been working on setting up a video commenting system, but I'm stuck on getting my "load more" button to function properly. Despite checking my PHP script and finding no errors, nothing seems to be happening when I click the button. As someon ...

Tips for transforming a string into an object using AngularJS

Here is a string I'm working with: $scope.text = '"{\"firstName\":\"John\",\"age\":454 }"'; I am trying to convert it into a JavaScript object: $scope.tmp = {"firstName":"John","age":454 }; Please note: J ...

How to deselect a checkbox in next.js when another one is selected

I'm looking to create a navigation system where clicking on a button scrolls to a specific section. However, I'm wondering how to deselect three inputs when one is selected in next.js using the code below. Do I need to modify each menu item indiv ...

What steps should I take to ensure my sanity studio updates are reflected in my nextjs app?

Upon configuring my sanity studio with the initial data, everything seemed to be working well. However, when I try to make changes and publish them, the next app still displays the previous data. Even after deleting some information in the studio, the upda ...

Discover pictures that perfectly match the optimal container dimensions

Looking to develop a function that can determine the best fitting image based on its size relative to its container. The image should not exceed the dimensions of the container either in width or height. The selected image should have minimal empty space ...

Retrieve information from the input field and then, upon clicking the submit button, display the data in the

When a user inputs their name, email, subject, text, and telephone number, I want to send an email with that data. The email will be sent to a specific email address, such as [email protected]. This process is crucial for a hotel website, where the em ...

The script fails to load when utilizing jquery/ajax

After the page loads, I am attempting to dynamically add a script into a div using ajax/jquery. This particular script is sourced from a CPM network and is responsible for loading a banner. However, when I try to load this script through ajax post-page lo ...

Class components in React can also utilize the `useSelector` hook from Redux. Here's a guide on how to

How can I access data from my Redux store without encountering any errors? For function-based components, I typically use the following method: ... import { useSelector } from 'react-redux'; export default function App(...){ ... const som ...

Generate an inclusive list featuring li elements with intricately detailed content within each

Currently, I am in the process of developing a web component and here is my code snippet: <ul class="list-with-arrow${this.inverse ? '--inverse' : ''}"> ${this.listData.map((item, index) => { ...

Divs that can be sorted are being shifted downwards within the swim lanes

JavaScript code snippet here... CSS code snippet here... HTML code snippet here... ...

The Google Books API has encountered an authentication error with status code 401

Trying to access public data using the Google Books API locally: An error occurred with the authentication credentials. It seems that an OAuth 2 access token, login cookie, or another valid authentication credential is missing. For more information, visit ...

What's causing these line break errors to occur for me?

When working in VSC, I have configured ESlint with the Airbnb settings. To test it out, I ran this code: const a = 'a'; const b = `${a}Hi`; const d = function () { return b; }; d(); The issue arises with the linebreaks: Expected linebreaks t ...

Balancing website speed with capturing product impression

I've been tasked with capturing impressions of all the products visible in the viewport on a website that features thousands of products. To achieve this, I implemented a directory and utilized the IntersectionObserver, which was referenced within the ...

Is there a way to deactivate all dot inputs on number type input in vue.js 2?

Here is an example of my HTML code: <div id="app"> <input type="number" v-model="quantity"/> </div> This is how my Vue component looks: new Vue({ el: '#app', data: { quantity: '' }, watch: { quanti ...

React functional components can utilize switch cases for conditional logic

I am attempting to create a multi-step form using a switch case, but for some reason, changing the state with nextPrev does not update the case. export const FormSteps = ({items, pending}) => { const [step, setStep] = useState (2) const nextS ...

Recognizing a component through various page loads

The title of this question may not be the best, but I hope my explanation clarifies what I'm trying to achieve. It's 4AM, so please forgive any confusion in my message. What I want to do is identify if a user-selected element appears on any page ...

Creating a new variable and then trying to access its value (which has not been defined)

I'm encountering an issue while trying to define a const variable called filteredRecipes and use it to setState. The problem is that the console is showing an error message saying that filteredRecipes is undefined, Uncaught ReferenceError: filteredRe ...