How to retrieve the old and new values of a store state - vue.js

In my component I am trying to get the old value and new value of a particular array of objects that is assigned in the vuex store state as followed. However when I newArray and oldArray return the same array of objects.
I understand from the documentation the following but I dont understand what is the best way to retrieve the different versions.
Note: when mutating (rather than replacing) an Object or an Array, the old value will be the same as new value because they reference the same Object/Array. Vue doesn’t keep a copy of the pre-mutate value.
here how I am trying to do it right now in the component
export default {
name: 'O1_OrderBook',
watch: {
'$store.state.orderBookSell': {
deep: true,
handler (newArray, oldArray) {
console.log(newArray,oldArray)
}
}
},
}

let say when you create an array/object in Javascript,
var arr = [1,2,3];
This creates an array in the browser's memory. But what arr variable contains is not the entire array value, it contains the reference to the array in browser memory. You could think of it as arr contains an address that can point you to the real array value in browser's memory.
if you do
var arr = [1,2,3];
var arr2 = arr;
arr2[0] = 4;
console.log(arr); // you would get [4,2,3];
editing arr2 changed arr too. Because they both point to the same array in browser memory.
That's what "the old value will be the same as the new value because they reference the same Object/Array" means.
The same principle applies to object as well. In Javascript, an array is just a special type of object.
to retrieve the different versions of the array in the watcher, you must clone it to set it as a new array everytime you mutate it.
for e.g.,
state.orderBookSell = [...state.orderBookSell];
BUT..... [...array] is shallow cloning, not deep cloning. And that's a problem. You have an array of objects. Remember that object also have the same rules. They are passed by reference. So you gotta do a deep clone, so that all the objects is cloned as well.
using lodash cloneDeep method for deep cloning
state.orderBookSell = _.cloneDeep(state.orderBookSell);

Based on Jacobs answer this is what I re-arranged in my component as to get it to work.
I created a computed variable in the component that deepClones the particular state array of objects.
computed: {
orders () {
return _.cloneDeep(this.$store.state.theArray)
},
},
and then setup a watch for that computed variable
watch: {
orders (newValue,oldValue) {
console.log(newValue,oldValue)
}
}

Related

Vue: replacing data object entierly?

I know that if I want to add a property to a data object, I use Vue.set(). I did this in my created() lifecycle method ... a new field will be added to the myObject. But what about if I wanted to make an api call and completely replace the existing data object of myObject? Can I do what I am doing in updateMore() method or is there another way to handle this?
data() {
return {
myObject: {}
}
},
async created() {
let apiData = await axios.get('http://myurl.com');
this.$set(this.myObject, 'name', apiData.name);
},
methods: {
updateMore() {
let moreAPIData = await axios.get('http://myurl.com');
// will this completely override `myObject` with `moreAPIData` and still be reactive?
this.myObject = moreAPIData;
}
}
TL;DR:
This assignment of a new object is fine. Vue reactivity issues occurs when you add a new key to an object, because the object is the same, what changed was its content. Javascript compares objects by its address, so if I do
new MyClass() === new MyClass()
it returns false, because, even if they have the same content, they have different addresses.
To conclude, when you set a whole new object, Vue is able to track the difference, but when you change the content of one key, it can't.
Full boring text
You can read the whole documentation about reactivity in Vue here

Update entire object in an array of objects in Vuex store with Vue.set()?

I have a form that needs to reactively update an entire object in an array of objects in a state mutation. I know Vue.set() works well for updating a single property, but I'm curious if it works to just update the full object. Something like this:
Vue.set(state.objects, obj.id, newObject)
The use case would be for a form update where the properties that are modified would be inconsistent. I may also just be misunderstanding Vue.set() altogether. The docs are extremely brief on this, saying only:
When adding new properties to an Object, you should either:
Use Vue.set(obj, 'newProp', 123), or
Replace that Object with a fresh one. For example, using the object spread syntax we can write it like this:
state.obj = { ...state.obj, newProp: 123 }
Any advice on the best way to go about this would be greatly appreciated!
I was previously updating the object by finding the index and replacing it, but that seemed to lack reactivity. Example:
let index = state.objects.findIndex(o => o.id === obj.id)
if (index !== -1) {
state.objects[index] = new Object(obj)
return
}
state.objects.push(new Object(obj))

set array of data into mobx array show proxy objects

I'm using react js with mobx and I get data from api.
the data I get is array of objects.
when I set the data into mobx variable then I see array of proxy objects(not sure what the proxy says). I'm trying just to set the array of objects I get from api into mobx variable.
my store
class UserStore {
#persist #observable token = null
#observable tasks = []
#observable done = false
#persist #observable email = ''
constructor() {
}
#action
getTasks = async () => {
try {
let response = await Api.getTasks()
console.log('getTasks',response.tasks)
this.tasks = response.tasks
console.log('my new tasks',this.tasks)
} catch (e) {
console.log(e)
}
}
as you can see here in the first block('black') the data i get from api, then i set the respnse.tasks into this.tasks.
this.tasks = response.tasks
console.log('my new tasks',this.tasks)
You can convert proxy to JS:
import { toJS } from 'mobx'
// example
toJS(response)
It depends on how you want to observe the data.
"I'm trying just to set the array of objects I get from api into mobx variable"
is not really your end-goal.
If you want your observers to:
option a: react when the array reference change
= No care for values in the array.
Use #observable.ref tasks.
option b: react when the references of each value in the array change
= No care for the individual objects properties.
Use #observable.shallow tasks.
option c: react on the individual objects properties too
= Make everything observable, references and object properties
Use #observable tasks like you do.
Like indicated in the comments, mobx5 is using Proxy and some behaviour might differ compared to previous version.
More info: Mobx arrays, Mobx decorators, shallow observability
Note: you would need to give more details if this does not help you, like your react component code.
In my case, toJS was not working because I had a class with a variable with the type of another class. So, I needed to create new objects from the JSON:
parsedArray.map(
(a) =>
new MyObj1({
id: a.id,
myObj2: new MyObj2({
label: a.label,
}),
title: a.title,
})
);
If you run debugger, stop the debugger. It messes the mobx's flow.

dojo store memory: the data is being updated before using the put() call.?

Dojo tookit version: 1.9.3
I have started learning dojo/store/memory from here.
https://dojotoolkit.org/documentation/tutorials/1.8/intro_dojo_store/
I tried to run the example in the tutorial with some modification.
Ran the get() (var oldjim = employeeStore.get("Jim");) call to check the value in the memory store before making a put() call.
I can see that the data has already been changed.
// retrieve object with the name "Jim"
var jim = employeeStore.get("Jim");
// show the department property
console.log("Jim's department is " + jim.department);
// iterate through all the properties of jim:
for(var i in jim){
console.log(i, "=", jim[i]);
}
// update his department
jim.department = "engineering";
// START *** Modified code
// Get the old data for "jim"
var oldjim = employeeStore.get("Jim");
// Displays the OLD data before making a put() call to the store.
console.log("oldJim's department is " + oldjim.department);
// output "oldJim's department is engineering"
// END *** Modified code
// and store the change
employeeStore.put(jim);
Is this the behaviour of the dojo/store/memory?
This is more behaviour of JavaScript than dojo/store/Memory.
Have a look at the implementation of dojo/store/Memory::get(id) method. It returns an object stored in data property array. In JavaScript objects are passed by reference and that means jim and oldjim both points to the same place in the memory. You can easily check it (jsFiddle):
console.log(jim === oldjim); // true
To avoid this behaviour you can modify dojo/store/Memory class so it returns a copy (clone) of the object:
require(["dojo/_base/declare", "dojo/_base/lang", "dojo/store/Memory"], function(declare, lang, Memory){
var ModifiedMemory = declare(Memory, {
get: function(id) {
return lang.clone(this.data[this.index[id]]);
}
});
});
In this case (using ModifiedMemory instead of Memory), jim === oldjim will evaluate to false, because they're not the same object anymore (i.e. there are 2 copies in the memory now) and therefore dojo/store/Memory::put() will work as you expect it to work.
See ModifiedMemory class in action at jsFiddle.

What WinJS.Application.sessionState really stores

In my Windows Store App I store data directly in the sessionState object so I don't need to move data there later on. In one case I store an object that has accessor methods for variables that are declared in the containing scope like so:
(function ()
{
var a = [];
var index = -1;
WinJS.Application.sessionState.data =
{
add: function (item)
{
index = a.length;
a.push(item);
},
currentItem: function ()
{
return a[index];
}
};
})();
My question is if the sessionState object will store a and index since they're scope referenced or not since they're are not really in it.
You can use "data" to manipulate "a" and "index" until the first time your app was suspended. Any data stored in the sessionState object is automatically serialized to disk when your app is suspended. Functions will be removed. After resuming, we lost 2 functions "add" and "currentItem".
See more: http://msdn.microsoft.com/en-us/library/windows/apps/hh440965.aspx