Can I make a Mobx observable with a custom getter? - javascript

I have a need for a Mobx observable object which simply returns whichever property is asked of it as a string. For example, myObs.description would simply return the string "description". With a normal JS object, I would use a proxy with a custom getter, like so:
new Proxy({}, {
get: (obj, prop) => {
return prop.toString();
}})
I can't do this with Mobx as the API uses a Proxy object that I can't update.
Is there a way to use Mobx to create an observable object that can have a custom computed value for any and every property passed to it?

Related

How to update/delete ref value in VueJS 3 composition api?

I'm using a ref value to be able to only execute a click event if the ref value is changing.
For example, if I want to update/del the array inside let myRef = ref([]);, do I just drill inside the proxy and do the operations like that :
selectedElements.value.push(3);
which returns
Proxy {0: 3}
or what is the correct way to update/del the ref.value ?
export default {
setup() {
let myRef = ref([]);
return {
myRef
};
},
};
You've got it under control. This is how I manage state.
In setup, I list all of my refs or reactive objects. I'll use computed properties if I feel like it to do various crosschecks on my refs or state, e.g. do I have records, how many, etc.
I'll write small functions that will handle state changes/mutations in a local component. If I need to use a watcher on state (ref or reactive object), then I'll do that and use it to call a method/action. You can only update ref inner values by accessing the ref object's single property .value which points to its inner value. I'm sure you know this, but the ref is a wrapper. IN your setup method, you'll got to unwrap a ref to change its inner value, but this isn't true with a reactive object - wherein you can just set that value as per a normal Vue2 data value.
So, yes, in your example, you can mutate your myRef array however you need by accessing its value property. Again, a good way to do this would be to define a method that does the thing that you need done, adding to the array. And, use a computed property to check for whatever it is that is your conditional that is allowing or denying your click event.
const datas=ref({
id:0,
name:'test',
age:20
})
delete datas.value.id
console.log(datas.value) //{name:'test',age:20}

Vuex getter not updating on state changes

I can't get a vuex getter to update, when a state object changes.
I'm trying to get a vuex getter to return a total sum of the values in a state object, with a reduce function.
// Mutation
state.shippingFees[method.sellerId] = method.fee; // state.shippingFees = { 1: 1900 }; etc.
// Getter
totalShippingFees: state => Object.values(state.shippingFees).reduce((total, fee) => total + fee, 0)
After setting a shipping fee key-value pair in the state.shippingFees object, the getter still just returns 0.
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method
Hence the way you are updating above object it is not reactive. Hence your values are not updated in the getter. To know more about vue reactivity read here.
For making it reactive you need to use $set or object.assign
Update your mutation as below =>
Vue.$set(state.shippingFees,method.sellerId,method.fee);
Vue cannot detect property addition or deletion
Use Vue.set(state.shippingFees, method.sellerId, method.fee)
More details here
Refer to the official documentation Caveats in Vue. You can't change the array elements by index. There is to ways to achieve the same with reactivity as given below
state.shippingFees.splice(index, 1, val)
//using the splice method, replace the element at index
or. alternatively, you can use
Vue.set(state.shippingFees, index , val)
//You can also use the vm.$set instance method, which is an alias for the global Vue.set method
vm.$set(vm.items, indexOfItem, newValue)
Vue can not detect the change when you directly update the item using the index, or modify the array length property.

What is the issue with state being an array in react?

Started learning react but made my state as an array.
my state was not getting properly update when i did something like
this.setState(state => [newItem, ...this.state])
above statement was converting an array to integer indexed object
I was getting a warning
index.js:2178 Warning: App.state: must be set to an object or null
seems like there is some check in react-dom code like this
if (state && (typeof state !== 'object' || isArray(state))) {
link
why is this happening
what are the issues i can face if i use the state as an array or something else than object or null
setState accepts objects, plain and simple. If you call this.setState(['a','b']) it will convert your array to an array-like object. Why? Because React allows that elsewhere in your code you should be able to call this.setState({data: 'something'}) without having that fail. For reference this.state would now look like this:
{
0: 'a',
1: 'b',
data: 'something'
}
Why did you get a warning? Because React performed this conversion, from array to array-like object, behind the scenes, and wants to let you know that things have changed. For example, after setting state to an array, you won't be able to call this.state.map ... or any other array methods.
If you need to store an array in state, set it to an object property: this.setState({arrayData: ['a', 'b']}). In general it's a good practice to wrap your data in object properties, because you will certainly be lifting state up as you develop your application.

mobx extendObservable doesn't work as expected

I am trying to use extendObservable to add more properties on observable state but it doesn't work. Below is my code
var store = mobx.observable({
property: {}
});
mobx.autorun(function () {
console.log("render:"+store.property.a);
});
store.property = {a:1};
extendObservable(store.property, {a:2});
store.property.a=3;
The output is:
render:undefined
render:1
I initialised a store with a property object. I want to add a as a observable state under property but I didn't get the autorun executed after using extendObservable method. I expected the value 3 got printed but it didn't. What's wrong with my code? Is it the correct way to use extendObservable method?
You can read why this doesn't work in the Common pitfalls & best practices section of the documentation:
MobX observable objects do not detect or react to property assignments
that weren't declared observable before. So MobX observable objects
act as records with predefined keys. You can use
extendObservable(target, props) to introduce new observable
properties to an object. However object iterators like for .. in or
Object.keys() won't react to this automatically. If you need a
dynamically keyed object, for example to store users by id, create
observable _map_s using
observable.map.
So instead of using extendObservable on an observable object, you could just add a new key to an observable map.
Example
var store = mobx.observable({
property: mobx.observable.map({})
});
mobx.autorun(function () {
console.log('render:' + store.property.get('a'));
});
store.property.set('a', 2);
setTimeout(function () {
store.property.set('a', 3);
}, 2000);
Please read this page https://mobx.js.org/best/react.html very minutely. There are some laws onto the usage of dynamic properties added using extendObservable. The one which you are "observing" (pun intended) is documented on that page in the section Incorrect: using not yet existing observable object properties. The right way to do this is also mentioned in the subsequent section.
Basically, your tracking functions must use the get method of the observable to ensure it gets invoked again on state mutations. The fixed code is -
mobx.autorun(function () {
console.log("render:"+store.property.get("a"));
});
extendObservable(store.property, {a:2});
store.property.set("a", 3);
So you need to use get/set with extendObservable extended objects.

knockoutjs undo ko.mapping.fromJS

I am aware of there is a function ko.utils.unwrapObserable() but it doesn't seem to unwrap an observable that is mapped by the ko.mapping.fromJS() at all:
console.log(listing);
listing = ko.utils.unwrapObservable(listing);
console.log(listing);
And I get the following output:
Object { __ko_mapping__={...}, title=c(), remote_id=c(), more...}
Object { __ko_mapping__={...}, title=c(), remote_id=c(), more...}
Reason I am asking for this is related to another question, basically listing is an instance of a class, which has methods reference its variables, the problem is after listing is mapped to an observable and the class methods will fail because the variables become methods.
My question is, is there a function for me to undo the mapping?
What you need is the ko.mapping.toJS function (see in documentation).
It does is the exact opposite of the ko.mapping.fromJS so it turns an object with observable properties to a plain JavaScript object without any observables:
console.log(listing);
listing = ko.mapping.toJS(listing);
console.log(listing);
Object { title="..,", remote_id="...", more...}
The actual implementation of the ko.mapping.toJS is that it recursively walks through your object properties and calls ko.utils.unwrapObservable on each of them.

Categories

Resources