Rich-Harris / Statesman

The JavaScript state management library

Home Page:https://github.com/Rich-Harris/Statesman/wiki

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

When observing an array of objects the returned old value is the same as the new value

stevenwaller opened this issue · comments

This library is great! Sorry to dust this off after a couple years, but I ran into one issue. Let me know if this is just the expected behavior.

When I observe an array of objects and update a value on one of those objects, the old value that is returned is actually the new value.

Observing the specific value of the object in the array works as expected.

var state = new Statesman({
   user: {
      name: 'Alice',
      friends: [ 
         {
            name: 'Bob',
            age: 20
         }
      ]
   }
});

// oldValue is the same as newValue
state.observe( 'user', function ( newValue, oldValue ) {
   console.log(oldValue); // user.friends[0].age = 21 (instead of 20)
   console.log(newValue); // user.friends[0].age = 21
}, {init: false});

// this works as expected
state.observe( 'user.friends[0].age', function ( newValue, oldValue ) {
   console.log(oldValue); // 20
   console.log(newValue); // 21
}, {init: false});

// change Bob's age
state.set( 'user.friends[0].age', 21 );

Here is a jsFiddle - type in a new value and hit submit to see it in action.

There are a problem with this idea. The library would need to make a deep copy of the state before it change its value because the state is an object.

If you watch the "user.friends[0].age", the age state is a number, and in JS, it will pass by value in a function.
In the callback, newValue is your second parameter of state.set call, and oldValue is "user.friends[0].age" (a number, passed by value).

Watching "user", it is an object, and objects are passed by references, not value. Changing the object inside the library.
In the callback, newValue is the state object (passed by reference), and the oldValue is state too (passed by reference). The same object on two parameters.

Ah, that makes sense! Thanks for walking me through it, I figured there was a good reason.