'Watch Vue 3 global variable for changes

I set a provider in my main.ts file:

app.provide('currentPage','test1')

And then inject it in a component Home.vue:

inject: ['currentPage'],

I then can update it and show it in that component without a problem using {{ currentPage }}.

But I want another component DeepNestedComponent.vue to be able to edit it, and Home.vue to be aware of the change.

When I inject the same provider in DeepNestedComponent.vue, I can edit and display in the component, but Home.vue is not aware of the change and {{ currentPage }} still display 'test1'.

How can I do that?



Solution 1:[1]

This pattern is designed only to pass some property from grandparent component to grandchild one, your case needs a shareable state based on Vuex or a composable function, let's build a solution based on the second approach :

define the composable function :

usePagination.ts

import {  ref } from "vue";

const currentPage=ref('test')

export default function usePagination(){

  function setCurrentPage(val:string){
      currentPage.value=val;
 }

return {currentPage, setCurrentPage}
}

DeepNestedComponent.vue

import usePagination from './usePagination'
...
setup(){
  const { setCurrentPage} =usePagination();

  // then use setCurrentPage to update the page

}

Home.vue :

import usePagination from './usePagination'
...
setup(){
  const { currentPage} =usePagination();

  // now you could receive the changes made in the other component.
  return {
       currentPage // expose it to the template 
   }
}

Solution 2:[2]

provide/inject is meant strictly for passing something down the hierarchy (kind of similar to dependency injection). It does not mutate/decorate the given target. This means if you provide a string, it will be consumed (injected) as a string, and a string by itself is not reactive.

If you want it to be reactive, you need to provide a reactive object or reference:

<script>
  import {defineComponent, ref, provide} from 'vue';
  import Parent from "./Parent.vue";
  
  export default defineComponent({
    setup (){
      const msg = ref("Hello World");
      provide("message", msg);
      return {msg};
    },
    
    components: {
      Parent
    }
  });
</script>

complete example

Solution 3:[3]

vue3 Reactive/Watchable Global Variables

In main.js

import { ref } from 'vue';
app.config.globalProperties.$currentPage = ref("Page 1");

Watch the variable in some file (Home.vue)

<template>
    <div>{{ currentPage }}</div>
</template>
<script>
export default {
  name: "App",
  data(){
     return {
         currentPage: "test1"
     }
  },
  mounted() {
    this.updateCurrentPage()
  },
  watch: {
    '$currentPage.value':{
      handler: function () {
          this.updateCurrentPage()
      }, deep: true }
  },
  methods: {
    updateCurrentPage(){
      this.currentPage = this.$currentPage.value
    }
  }
}
</script>

Change the variable in another (DeepNestedComponent.vue)

<template>
   <div>
      <button @click="changePage()">changePage</button>
   </div>
</template>
<script>
export default {
  name: 'DeepNestedComponent',
  data(){
    return {
      pageCount: 0
    }
  },
  methods:{
    changePage(){
      this.pageCount    = this.pageCount + 1
      const pageValue   = `Page ${this.pageCount}`
      this.$currentPage = pageValue
    }
  }
}
</script>

Found this solution from "Vue3 reactive components on globalProperties" when I was looking to set a global variable for my websites color scheme

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Xinchao
Solution 3