vue3 reactive unexpected behaviour

血红的双手。 提交于 2021-02-10 21:13:22

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!