vue.jsvuejs3

How to use provide in Vue's optional API to achieve bidirectional data transfer of basic data types?


There are two components. I want to implement two-way data transmission between parent and child by providing a responsive object. However, I found that the object type can correctly implement responsiveness, but the basic data type cannot. Why? My personal guess is that the optional API will trigger automatic unpacking for basic data types, but it will not be triggered in the setup function, thus causing inconsistency.

//parent
<script>
import Child from './Child.vue'
import { computed } from 'vue'

export default {
  components: { Child },
  data() {
    return {
      message: {"name":"hello"},flag:false
    }
  },  methods:{
change()
{
  this.flag=!this.flag
}
  },
  provide() {
    return {
      message: this.message,flag:this.flag
    }
  }
}
</script>

<template>
  <div>
    <input v-model="message.name">
    <button @change="change">click</button>     
    {{ flag}}
  </div>
  <Child />
</template>
//child
<script>

export default {
  inject:["message","flag"],
  methods:{
change()
{
  this.flag=!this.flag
}
  }
}
</script>

<template>
  <div>
  <input v-model="message.name">
  <button @change="change">click</button>   
      {{ flag}} 
  </div>

</template>

Attempt: I consulted the official documentation and found that data() returns responsive objects, and basic data types can be passed correctly using the setup function.

Expected result: Bidirectional binding can be achieved when using optional provide to pass basic data types


Solution

  • This is fundamental to JavaScript. A value that isn't passed by reference cannot be updated this way:

    let foo = 1;
    function change(value) {
      value++;
    }
    change(foo);
    console.log(foo === 1);

    This requires to use a object that holds a reference to a value, this the purpose of the ref pattern:

    let foo = { value: 1 };
    function change(valueRef) {
      valueRef.value++;
    }
    change(foo);
    console.log(foo.value === 2);

    That a value is a property on this and not a variable doesn't make it different. flag: this.flag is the same as flag: false because provide is called once on component creation.

    This requires to use reactive object that holds a reference to a value. ref won't work for this purpose in options API because it would be unwrapped when used in data, instead it should be an object:

      data() {
        return {
          flag: { value: false }
        }
      },
      provide() {
        return {
          flag: this.flag
        }
      }