Update data when prop changes. Data derived from prop

I just came across this problem again and thanks to @ottopilot comments above I tried something else:

  • On the parent component I added a dataReady property set to false by default.
  • In the parent component markup for the child component I added a v-if="dataReady". This needs to be v-if not v-show.
  • Within the parent component I am setting the dataReady property to true once the data is loaded (using Vuex state status properties).
  • When dataReady is set to true the child component gets initialized with the correct state and is reactive.

This works and seems a little more elegant than watching props in the child.

1 Like

How exacly did you use the Vuex state properties in you parent component? Inside a beforeMount perhaps? Could you paste this part of your code please?

Hi Elcano,

Not all the code but the important parts:


<script>
export default {
  data () {
    return { dataReady: false }
  },
  computed: {
    dataStatus () {
      return this.$store.getters['dataStatus']
    }
  },
  watch: {
    dataStatus () {
      this.dataStatus && (this.dataReady = true)
    }
  }
}
</script>

Again, put a v-if on the markup bound to dataReady.

This is not fully functional code. Just a summary.

Many thanks @grantcarthew, I can see your solution still use a watcher. Anyway my problem was related with object props, it is solved now.

Good stuff. The watcher is still needed because the dataStatus computed property is not used in the markup. I tried removing it and the reactivity stops functioning.

I have a related question here – Best way to use forms with local state (using v-model) and sync to vuex store on save?
Maybe you guys can shed some light?

One more solution to do this thing is by using Computed getter and setter in child component to bind using v-model.
Example: https://jsfiddle.net/Jayesh_v/4zzz72a0/
App.vue

   <template>
      <div id="app">
        <h2>Parent {{ msg }}</h2>
        <child-component v-model="msg"></child-component>
      </div>
    </template>
    <script>
      import ChildComponent from './ChildComponent.vue'
    export default {
      name: 'app',
      data () {
        return {
          msg: 'Parent Message'
        }
      },
      components: {
        ChildComponent
      }
    }
    </script>

ChildComponent.vue

<template>
  <div>
    <div style="border: 1px solid #000000">
      <h1>Child</h1>
      <input type="text" v-model="childData">
    </div>
  </div>
</template>
<script>
  export default {
    props: ['value'],
    data () {
      return {}
    },
    computed: {
      childData: {
        get () {
          return this.value
        },
        set (newValue) {
          this.$emit('input', newValue )
        }
      }
    }
  }
</script>
2 Likes

dude literally the only solution that I found without Vuex that works thank you!

Be careful with this, most Microsoft browsers will not accept “setting” a property. I’ve run into this before

I agree, it’s a hack. Thanks for filling us in on what you did.

How about this situation?

I have 2 components and 1 page:

MainComponent.vue

<template>
<v-layout 
  justify-center 
  row 
  wrap>
  <v-flex 
    xs12 
    sm12 
    md12>
      <v-card light>      
        <v-container>
          <h3 class="h3">Дополнительные настройки пользователя</h3>
          <div class="notification-text"> Укажите, пожалуйста, требуемые настройки:</div>
           <v-container class="setting-switches">
             <sms-notifications
               :sms-place="smsPlaceData"
               @updateSmsPlace="handleSmsPlaceUpdate"
             />
             <mail-notifications
               :mail-place="mailPlaceData"
               @updateMailPlace="handleMailPlaceUpdate"
             />
        </v-container>    
      </v-container>
      <!--Конец диалога-->
      </v-card>
    </v-flex>
  </v-layout>
</template>

<script> 
import SmsNotifications from '~/components/settings/SettingsSmsNotification'
import MailNotifications from '~/components/settings/SettingsMailNotification'

  export default {
    components: {
    SmsNotifications,
    MailNotifications
  },
  props: {
    smsPlace: {
    type: Boolean,
    required: true
  },
  mailPlace: {
    type: Boolean,
    required: true
  }
},
data: () => ({
  smsPlaceData: false,
  mailPlaceData: true
}),
methods: {
  handleSmsPlaceUpdate(newValue) {
    this.smsPlaceData = newValue
  },
  handleMailPlaceUpdate(newValue) {
    this.mailPlaceData = newValue
  }
 }
}
</script>    


**And 2-d component:**
ChildComponent.vue

<template>
  <div v-if="smsPlace">      
    <v-switch 
       label="Статус смс-уведомлений: Подключено"                        
       color = "blue"
       v-model="sms"
    ></v-switch>
  </div>
  <div v-else>
     <v-switch 
        label="Статус смс-уведомлений: Отключено"                        
        color = "grey"
        v-model="sms"
     ></v-switch>
   </div>
</template>

<script>
  export default {
    props: {
      smsPlace: {
      type: Boolean,
      required: true
    }
  },
  computed: {
     sms: {
       get() {     
          return this.smsPlace
     },
     set(value) {    
        this.$emit("updateSmsPlace", value)
     }   
    },
 }
}
</script>

My page
Profile.vue

<template>
  <div id="app">
    <v-app id="inspire">
      <profile-settings
         :smsPlace="smsPlace"
         :mailPlace="mailPlace"/>
   </v-app>
  </div>
</template>

<script>
   import ProfileSettings from '~/components/settings/ProfileSettings'

   export default {
     data: () =>({
       smsPlace: true,  
       mailPlace: false, 
     }),
     components : {
       ProfileSettings
     }
   }
   </script>

How can I get smsPlace data from page Profile.vue in component MainComponent.vue??

You need to again $emit smsPlaceData to your parent Profile.vue page and handle it in Profile.vue.

handleSmsPlaceUpdate(newValue) {
    this.smsPlaceData = newValue
    this.$emit("updateSmsPlaceProfile", newValue)
}

Solution of this case is here:

this is a good practice to set updated data from parent? how about if we have a v-model connected in a data ?

<input v-model='name'>
name: ''

The flow will be:

  1. User add something in name
  2. emit name to parent
  3. Later parent set name value updated
    I repeat, this is the best practice? @LinusBorg