问题
i've a reactive object, on the save function i call toRaw in order to remove de object reactivity, but, when i change the reactive object props, also the group object is changing.... How???
const newGroup = reactive<Group>({ id: undefined, name: '', enabled: false })
const handleSave = () => {
const group = toRaw(newGroup)
persist(group).then(() => {
newGroup.id = undefined
newGroup.name = 'demo'
console.log(isReactive(group)) //say false but the group.name value is demo
})
}
destructuring the reactive obj as const group = { ...newGroup } looks like it works, still no understand why toRaw is not working.
EDIT:
the whole problem comes from a sense of lack of control when dealing with reactive objects for example:
the following cumputed object retrieves a list from the store which is then drawn in the template as rows of a table at which click the selected element is changed of state. in the function I am forced to deconstruct records to avoid that the modification trigger the change of enabled before it is persisted Is there a better way? Should I make readonly groups?
//in setup()
const groups = computed <Group []> (() => getters ['registry / groups'])
const toggle = (record: Group) => {
const group = { ...record }
group.enabled = !group.enabled
persist(group)
}
//in template
<a-menu-item @ click = "toggle (record)">
回答1:
This is expected behaviour, but the description may not be detailed enough and cause some confusion.
according to toRaw docs
Returns the raw, original object of a reactive or readonly proxy. This is an escape hatch that can be used to temporarily read without incurring proxy access/tracking overhead or write without triggering changes. It is not recommended to hold a persistent reference to the original object. Use with caution.
What is happening when you use toRaw
, is that you're getting back the original object that you passed into the reactive
function without the Proxy. That means that the object is the same object (o
and g
in example 👇). toRaw
only allows you to "escape" the reactivity/listeners bound to the object if you use toRaw
instead of the Proxy itself. Even though the objects update, the reactivity (trickling down to DOM) is not applied.
Here is an example:
let o = {name:"Ja Ja"};
let m = Vue.reactive(o);
let g = Vue.toRaw(m);
console.log(o.name, m.name, g.name);
g.name = g.name + " Ding Dong";
console.log(o.name, m.name, g.name);
console.log("Is 'm' same as 'o'? ", m == g);
console.log("Is 'g' really just 'o'? ", o == g);
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
If you want to create an object that will not update the original object, the simplest way is to clone it.
Most of the time you can use JSON.parse(JSON.stringify(o))
. The caveat is that you get into issues if you have cyclical references (ie. {a:b, b:a}
).
Another option is to use destructuring const copy = {...o}
. The caveat here is that it is a shallow copy. Example o = {a:{b:"I will update"}, c:"I won't"}
, where copy.a
is still same object as o.a
That would then look like either
let o = {name:"Ja Ja"};
let m = Vue.reactive(o);
let g0 = {...Vue.toRaw(m)}; // destructure
let g1 = JSON.parse(JSON.stringify(Vue.toRaw(m))); // clone with json
Alternatively, you can also use some library that handles cloning object for you.
回答2:
You have a misunderstanding of reactivity here. A reactive object connects the data with DOM, not between javascript objects, i.e. when a property of the reactive object is updated, the DOM is automatically updated.
toRaw returns the raw, original object of a reactive or readonly proxy, i.e group
and newGroup
are still the same object, the difference is that group
would not trigger a DOM update as newGroup
does.
Example, UI is updated when you update newGroup
, but updating group
won't trigger a DOM change. However the two objects still have the same reference, which is why group.name
and newGroup.name
are always the same.
Vue.createApp({
setup() {
const newGroup = Vue.reactive({ id: undefined, name: '', enabled: false })
const group = Vue.toRaw(newGroup)
return {
newGroup,
group
}
}
}).mount('#app')
<script src="https://unpkg.com/vue@next/dist/vue.global.prod.js"></script>
<div id="app">
<button @click="newGroup.name = 'reactive update'">reactive update</button>
newGroup Name: {{newGroup.name}}<br/>
<button @click="group.name = 'raw update'">raw update</button>
group Name: {{group.name}}
</div>
回答3:
it is normal that isReactive(group)
return false
.
Actually in your code we have const group = toRaw(newGroup)
=> group
is the original object of the reactive value newGroup
.
=> group
is a raw value, not a reactive value
if you tried isReactive(newGroup)
this will return true
来源:https://stackoverflow.com/questions/65737075/vue3-reactive-unexpected-behaviour