arqex / freezer

A tree data structure that emits events on updates, even if the modification is triggered by one of the leaves, making it easier to think in a reactive way.
MIT License
1.28k stars 56 forks source link

only last set() reflected in update? #18

Closed jwaala closed 9 years ago

jwaala commented 9 years ago

I have a frozen object called appData where I do the following as a test...

                    appData.set("xxx","XXX");
                    appData.set("yyy","YYY");
                    appData.set("zzz","ZZZ");

Similar to your json editor example

        // We are going to update the props every time the store changes
        listener.on('update', function( updated ){
            me.setProps({ store: updated });
        });

I have a listener where I

            listener.on("update", function (appDataNew) {
                console.log("UPDATE");
                me.setProps({appData: appDataNew});
                console.log(JSON.stringify(me.props.appData, null, "  "));
            });

In the console, I see the following. Only the the LAST of the 3 set items (zzz) is set. It does not matter what I am setting or how many, the last one is the only one to stick.

{
  "route": "",
  "name": "",
  "address": "70.112.68.147",
  "noop": "",
  "zzz": "ZZZ"
}

There has to be something dumb I am doing here.

aktxyz commented 9 years ago

This may help...

http://jsbin.com/xefutu/1/edit?js,console

works if I chain it ...

                    appData
                        .set("xxx","XXX")
                        .set("yyy","YYY")
                        .set("zzz","ZZZ");
arqex commented 9 years ago

Hi @jwaala

I also saw your email but I didn't have time to reply, although it's better to reply here so this can help somebody else. As @aktxyz said, it works when you chain it, but why? Don't forget that frozen objects are immutable and every time that you call set, you are calling it on the same object, nothing changes on it when you call it:

// Let's create a new freezer object
var store = new Freezer({}),
  appData = store.get()
;

// Update the data
var updatedData = appData.set({xxx: 'XXX'});

// appData didn't change at all
console.log( appData ); // Logs [Object object] {}

// And what is more important, appData is not part of the store anymore
appData == store.get(); // false

// On every update, freezer nodes return the updated object that will
// be on the store
console.log( updatedData ); // {xxx: 'XXX'}
store.get() == updatedData;

// It is not recommended to keep using a node that is not in the store, because
// it shares the listener with the new one, and that can create errors
var noStoreData = appData.set({yyy: 'YYY'});
console.log( appData ); // {}
console.log( noStoreData ); // {yyy: 'YYY'}

// The store is still the same, but the node will receive a false 
// update event on the next tick with YYY
console.log( store.get() ); // {xxx: 'XXX'}

// That's why chaining works, because you are always using
// the updated store
updatedData.set({yyy:'YYY'})
  .set({zzz: 'ZZZ'})
;

Also, the reason of you getting just one update event is that the update event is not triggered immediately. Since the event is used to refresh the UI, it waits until the current thread is finished so you can make many updates to your store sequentially and your view will be re-rendered only once with the final result. @aktxyz that's the nextTick concept that I tried to explain to you in the other issue page.

arqex commented 9 years ago

BTW, if you want to make multiple set at once, you can use an object as a parameter

appData.set({
  xxx: 'XXX',
  yyy: 'YYY',
  zzz: 'ZZZ'
});