Home > Mobile >  Why Vue 3 toRaw on props keeps reactivity?
Why Vue 3 toRaw on props keeps reactivity?

Time:04-11

I have a scenario, where I would like to lose the reactivity from the incoming props in a Child component to modify the props, however, I would also like to keep the Child component updated when the props change (keep this part of reactivity). This is very useful for forms/states where the data change can be cancelled/reverted.

One way of doing is to make a clone of the props. After looking up for the best way to do it, I decided to use toRaw().

When I tested the code I found some weird behaviour.

Scenario 1. If props are passed to toRaw() function and assigned to a new variable, they can be modified in the Child component and they keep the reactivity. - This is what I wanted, as there is no warning in the console, the props are not changing Parent data and when the Parent data does change, the props are updated.

Scenario 2. If props are destructively assigned while passed to toRaw() function, they lose the reactivity.

Scenario 3. If props are passed to ref() function and assigned to a new variable, they lose the reactivity.

So my questions are:

  • why scenario 1 works as intended?
  • why isRef() is telling me that the _props copy is not reactive (but it is)?
  • is this the right approach to achieve my required flow?
// App.vue
<script setup>
import { ref } from 'vue'
import Comp from "./Comp.vue";
  
const name = ref("John");

const nameChanged = () => {
  name.value = "Jane "   Math.random();
  console.log(name.value);
}
</script>

<template>
  {{ name }}
  <Comp @changeName="nameChanged" :name="name"></Comp>
</template>

// Comp.vue
<script setup>
import {ref, toRaw, isRef} from "vue";
const props = defineProps(['name'])

var _props = toRaw(props);
var n = ref(props.name);
_props.name = "it workss";
const isPropRef = isRef(_props);
</script>

<template>
  <div>
    <h1>
        {{ _props.name }}, {{n}}
    </h1>
    <h1>
        isPropCopyRef: {{ isPropRef }}
    </h1>
    <button @click="$emit('changeName')">
        Change name
    </button>
  </div>  
</template>

Vue SFC link

CodePudding user response:

why scenario 1 works as intended?

I think Daniel's answer answers this

why isRef() is telling me that the _props copy is not reactive (but it is)?

The isRef helper checks if the object passed is created with ref but the props are like an object created with reactive (also exists a isReactive helper)

is this the right approach to achieve my required flow?

No, the toRaw helper does not copy the proxy, so you are still mutating the props object by reference, but you will not see this in the vue devTools, warnings on console, or printing in the DOM because you remove the Proxy trigger from vue, you are mutating directly the raw object, what is a bad practice as it can lead to unexpected behavior and debugging difficulties.

you can use different approaches to achieve what you want. You can use watch, or writable Computed

I will give you an example using Writable computed but also works with the watch, the principles are the same, practice the principle of immutability and use events to mutate the parent data

Note: my example is thinking that you are going to use this component with 'v-model', if not your case just change the props and events, the important part is the computed

import { computed } from 'vue';

const props = defineProps({
  modelValue: {
    type: Object, // assuming you have multiples properties in an form
    required: true,
  }
})

const emit = defineEmits(['update:modelValue'])

const formData = computed({
  get: () => props.modelValue,
  set: (newValue) => emit('update:modelValue', newValue),
})
  • Related