Search code examples
vue.jsvue-componentvue-composition-api

Read and set Reactive object from another component in Vue 3 (composition API)


I am new to Vue and have a component A) Notification bar which has a Reactive object containing state, message. Then I want to show a div in another component based on the status of Reactive object in component A. Also I want to be able to change the Reactive object in component A from a method in component B. How do you achieve this?

Component A:

<template>
<div v-html="$notificationStore.message" v-if="$notificationStore.notify"></div>
</template>

<script setup>
import { reactive } from "vue";

const $notificationStore = reactive({
    notify: false,
    type: "",
    message: ""
});
</script>

Component B:

<template>
<div v-if="!$notificationStore.notify"></div> <!-- Should read "notify" from Component A"-->
<button type="button" @click="ShowNotification('Message to show')"/>
</template>

<script setup>
import { reactive } from "vue";

function ShowNotification(message){
   this.$notificationStore.notify = true;
   this.$notificationStore.Message = message;
};
</script>

Thank you very much!


Solution

  • You don't need to use $ for your data properties.

    Be sure that you understand the security risks bound with using v-html directive

    There are better ways to achieve the same result, without using v-html.

    UPDATE

    I have changed the example to cover using SFCs.

    You can pass your store to your components through props.

    <comp-a :store="myStore"></comp-a>
    

    Pay attention, that props are readonly. Since we pass an reactive object, you still can change the object properties from the component but this is a bad practice and is not recommended.

    It is recommended to keep any mutations to reactive state inside of the provider whenever possible.

    I would also suggest you to try using Pinia instead of developing your own custom store solution.

    Working playground

    const { createApp, reactive } = Vue;
    
    const myStore = reactive({
        notify: false,
        type: "",
        message: "",
        showNotification: function(message) {
          this.notify = true;
          this.message = message;
       }
    })
    
    const CompA = {
      props: ['store'],
      template: 'CompA: <div v-html="store.message" v-if="store.notify"></div>'
    }
    
    const CompB = {
      props: ['store'],
      template: `CompB:
        <div v-if="!store.notify">
          <button type="button" @click="store.showNotification('Message from CompB')">show</button>
        </div>`
    }
    
    const App = {
     components: { CompA, CompB },
     setup() {
      return  { myStore }
     }
    }
    
    const app = createApp(App)
    app.mount('#app')
    #app { line-height: 1.75; }
    [v-cloak] { display: none; }
    <div id="app">
    <comp-a :store="myStore"></comp-a><br/>
    <comp-b :store="myStore"></comp-b>
    </div>
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>