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>
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),
})