Reputation: 314
I have a VueJS component like this
const MyComponent = Vue.extend({
props: ["myHandle"],
computed: {
name: {
get: function() { return myHandle.getName() },
set: function(newName) { myHandle.setName(newName) },
}
}
})
The problem is that setting the computed property name
does not trigger VueJs to update the property. Meaning the old name is what gets displayed on re-render.
Now, I'm fairly new to VueJS and i can see how this might be code smell, but the thing is that the myHandle
property is actually a handle to access data in a WebAssembly module written in rust. The rust code is where most of my state/model/source of truth lives.
The two unsatisfactory solutions i have found thus far are:
Use a method to retrieve the name instead of a computed property. Basically add a getName
method to my component and change my template code from <p> Your name is {{ name }}</p>
to <p> Your name is {{ getName() }}</p>
. Which will cause VueJs to not cache. In the future I'm planing to do expensive computations meaning unnecessary performance cost
Copy state from the rust side to my component. This I do not want to do since I would get multiple source of truth.
So is there some way to:
A: Get VueJS to understand that myHandle.setName
is a mutating method that should cause name
computed property to be updated.
B: Manually trigger an update.
C Solve it in some other manner?
Upvotes: 4
Views: 14509
Reputation: 1443
I don't know how your implementation of setName() is, but if it is
setName(newName) { this.name = newName; }
you might have run into the problem described here: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
I would like to add that it is probably not a good idea to mutate myHandle as a prop, so I suggest that if you can refactor your code to use myHandle as a data property, to do so.
Upvotes: 2
Reputation: 415
There is a simple trick - dummy_toogle - just place it in your component data: {toggle:false} and in your getter/setter: get: function(){this.toggle; ... } set:function(name){...; this.toggle=!this.toggle;} Now vue will think that computed property is dependent on the toggle and recompute it if toggle is changed + you can make a very stupid callback and pass it to non-vue part of your app as update-trigger
Upvotes: 0
Reputation: 378
Computed properties are cached based on their dependencies (data, props and other computed properties). If I've understood correctly, your computed property won't be reactive cause the only dependency is myHandle, and I assume that will never change. Look at it this way, Vue can only check that getName is returning a different value by actually calling the method, so there's nothing to cache.
If the name can only be changed from your Vue component, you can set it as a data field and add a watcher to update the WebAssembly value. Otherwise, if the name can be changed from non-Vue sources and you must call the getName method from the handler every time, you will have to come up with your own mechanism (maybe using a debounce function to limit the number of calls).
Upvotes: 3
Reputation: 1811
I hope this example will help you
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="app">
<my-component :myhandle="myhandle">
</my-component>
<h2>Vue app Object: {{myhandle.name}}</h2>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script>
Vue.component('my-component', Vue.extend({
props: ["myhandle"],
data () {
return {
name:""
}
},
watch: {
myhandle:{
handler (val, oldVal) {
console.log('Prop changed');
// do your stuff
},
deep: true, // nested data
}
},
methods: {
chName(){
this.myhandle.name = "Henry";
}
},
template:'<div><h1>{{myhandle.name }}</h1><br><button @click="chName()">Cahnge name</button></div>',
}),
);
var app = new Vue({
el:'#app',
data (){
return {
myhandle:{
name:'Jack'
}
}
}
});
</script>
</body>
</html>
Upvotes: 0