Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js Changing props

I'm a bit confused about how to change properties inside components, let's say I have the following component:

{
    props: {
        visible: {
            type: Boolean,
            default: true
        }
    },
    methods: {
         hide() {
              this.visible = false;
         }
    }
} 

Although it works, it would give the following warning:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "visible" (found in component )

Now I'm wondering what the best way to handle this is, obviously the visible property is passed in when created the component in the DOM: <Foo :visible="false"></Foo>

like image 381
woutr_be Avatar asked Nov 24 '16 07:11

woutr_be


People also ask

Can we change the value of props in Vue?

The value of a parent property cannot be changed inside a component, and, in fact, the updated value will be lost if the parent re-renders for any reason. To update the parent property, what you should do is $emit the updated value and listen for the change in the parent.

Does changing props cause re render Vue?

The changed prop is not used by the render function so rendering should not be triggered. There are several use cases for props that don't require rendering updates. e.g.: The prop could be an id used to load data behind the scenes.

How do you access props in data Vue?

To access props in a Vue. js component data function, we can get them from this . to register the messageId prop. Then we get the initial value of the messageId prop with this.


2 Answers

Referencing the code in your fiddle

Somehow, you should decide on one place for the state to live, not two. I don't know whether it's more appropriate to have it just in the Alert or just in it's parent for your use case, but you should pick one.

How to decide where state lives

Does the parent or any sibling component depend on the state?

  • Yes: Then it should be in the parent (or in some external state management)
  • No: Then it's easier to have it in the state of the component itself
  • Kinda both: See below

In some rare cases, you may want a combination. Perhaps you want to give both parent and child the ability to hide the child. Then you should have state in both parent and child (so you don't have to edit the child's props inside child).

For example, child can be visible if: visible && state_visible, where visible comes from props and reflects a value in the parent's state, and state_visible is from the child's state.

I'm not sure if this is the behavour that you want, but here is a snippet. I would kinda assume you actually want to just call the toggleAlert of the parent component when you click on the child.

var Alert = Vue.component('alert', {   template: `         <div class="alert" v-if="visible && state_visible">         Alert<br>          <span v-on:click="close">Close me</span>       </div>`,   props: {     visible: {       required: true,       type: Boolean,       default: false     }   },   data: function() {     return {       state_visible: true     };   },   methods: {     close() {       console.log('Clock this');       this.state_visible = false;     }   } });  var demo = new Vue({   el: '#demo',   components: {     'alert': Alert   },   data: {     hasAlerts: false   },   methods: {     toggleAlert() {       this.hasAlerts = !this.hasAlerts     }   } })
.alert {   background-color: #ff0000; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script> <div id="demo" v-cloak>   <alert :visible="hasAlerts"></alert>    <span v-on:click="toggleAlert">Toggle alerts</span> </div>
like image 70
ArneHugo Avatar answered Oct 04 '22 17:10

ArneHugo


According to the Vue.js component doc:

When the parent property updates, it will flow down to the child, but not the other way around. So, how do we communicate back to the parent when something happens? This is where Vue’s custom event system comes in.

Use $emit('my-event) from the child to send an event to the parent. Receive the event on the child declaration inside the parent with v-on:my-event (or @my-event).

Working example:

// child

Vue.component('child', {
  template: '<div><p>Child</p> <button @click="hide">Hide</button></div>',
  methods: {
    hide () {
      this.$emit('child-hide-event')
    }
  },
})

// parent

new Vue({
  el: '#app',
  data: {
    childVisible: true
  },
  methods: {
    childHide () {
      this.childVisible = false
    },
    childShow () {
      this.childVisible = true
    }
  }
})
.box {
  border: solid 1px grey;
  padding: 16px;
}
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app" class="box">
  <p>Parent | childVisible: {{ childVisible }}</p>
  <button @click="childHide">Hide</button>
  <button @click="childShow">Show</button>
  <p> </p>
  <child @child-hide-event="childHide" v-if="childVisible" class="box"></child>
</div>
like image 31
François Romain Avatar answered Oct 04 '22 15:10

François Romain