2
votes

I'm stuck in the situation where my child component (autocomplete) needs to update a value of its parent (Curve), And the parent needs to update the one of the child (or to completely re-render when a new Curve component is used)

In my app the user can select a Curve in a list of Curve components. My previous code worked correctly except the component autocomplete was not updated when the user selected another Curve in the list (the component didn't update its values with the value of the parent).

This problem is fixed now but I get this 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: "value"

The description of this warning explain exactly what behavior I expect from my components. Despite this warning, this code works perfectly fine !

Here is the code (parts of it have been removed to simplify)

// curve.vue
<template>
  <autocomplete v-model="curve.y"></autocomplete>
</template>


<script>
  import Autocomplete from './autocomplete'

  export default {
    name: 'Curve',
    props: {
      value: Object
    },
    computed: {
      curve() { return this.value }
    },
    components: { Autocomplete }
  }
</script>


// autocomplete.vue
<template>
    <input type="text" v-model="content"/>
</template>

<script>
  export default {
    name: 'Autocomplete',
    props: {
      value: {
        type: String,
        required: true
      }
    },
    computed: {
      content: {
        get() { return this.value },
        set(newValue) { this.value = newValue }
      }
    }
  }
</script>

A lot of people are getting the same warning, I tried some solutions I found but I was not able to make them work in my situation (Using events, changing the type of the props of Autocomplete to be an Object, using an other computed value, ...)

Is there a simple solution to solve this problem ? Should I simply ignore this warning ?

2
in the doc: for a component to work with v-model you need to accept a value prop and emit an input event !!hannes neukermans

2 Answers

3
votes

You can ignore it and everything will work just fine, but it's a bad practice, that's what vue is telling you. It'll be much harder to debug code, when you're not following the single responsibility principle.

Vue suggests you, that only the component who owns the data should be able to modify it.

Not sure why events solution ($emit) does not work in your situation, it throws errors or what?

To get rid of this warning you also can use .sync modifier: https://vuejs.org/v2/guide/components.html#sync-Modifier

4
votes

you can try is code, follow the prop -> local data -> $emit local data to prop flow in every component and component wrapper.

ps: $emit('input', ...) is update for the value(in props) bind by v-model

// curve.vue
<template>
  <autocomplete v-model="curve.y"></autocomplete>
</template>


<script>
  import Autocomplete from './autocomplete'

  export default {
    name: 'Curve',
    props: {
      value: Object
    },
    data() {
      return { currentValue: this.value }
    }
    computed: {
      curve() { return this.currentValue }
    },
    watch: {
      'curve.y'(val) {
        this.$emit('input', this.currentValue);
      }
    },
    components: { Autocomplete }
  }
</script>


// autocomplete.vue
<template>
  <input type="text" v-model="content"/>
</template>

<script>
  export default {
    name: 'Autocomplete',
    props: {
      value: {
        type: String,
        required: true
      }
    },
    data() {
      return { currentValue: this.value };
    },
    computed: {
      content: {
        get() { return this.value },
        set(newValue) {
          this.currentValue = newValue; 
          this.$emit('input', this.currentValue);
        }
      }
    }
  }
</script>