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

Can't not listen to transact changes #36

Closed grassick closed 9 years ago

grassick commented 9 years ago
  var freezer = new Freezer({ x: 1 });
  var trans = freezer.get().transact();
  trans.x = 2;
  freezer.get().run();

  freezer.get().getListener().once('update', function(val) {
    console.log("Why does this get called?");
    });

Prints: Why does this get called?

I do a bunch of cleanup and fixing of the object tree after I detect a change, and I don't want that to trigger another update. So I'm trying to not listen to changes during the running of the transaction. However, even if I add the listener after the transaction is run, it still fires the update. Is there any way to avoid this and to not listen to the changes?

arqex commented 9 years ago

Hi @grassick

When you make a change, the update events are triggered on the next tick. That lets you make a bunch of changes just triggering one event. On any freezer node, if getListener has not been called previously to the modification, the update event is not triggered.

But, in the root node there is always a listener ready to allow to listen to the Freezer object itself:

var freezer = new Freezer({x:1});
freezer.on('update', function(){
  console.log('This also get called.');
});

When you do freezer.get().getListener() after you make the change, you are getting the same listener that was already listening to that change, and it triggers the event on next tick.

http://jsbin.com/qisozo/2/edit?js,console

Maybe you can add a condition to handle that first update that you don't want to listen, or bind your callback inside the first event callback with once.

grassick commented 9 years ago

Thanks for discussing this!

I really do need to run the frozen results through a series of transforms after an update. I did some fiddling and it seems that if I make a new freezer with the frozen object, the old one stops listening completely. That is:

freezer = new Freezer({ x: 1 })

freezer.on 'update', (u) -> 
  console.log("printed once")

fo = freezer.get()
fo2 = fo.set(x:2)

freezer2 = new Freezer(fo2)
console.log freezer2.get() == fo2 # true. Is same object.

freezer2.get().set(x:3) # Doesn't trigger update of first freezer!

(apologies for the coffee script)

Is this official behaviour that I can rely on?

arqex commented 9 years ago

If I got it right, whenever you update the store, you need to make some transformations to the data that you don't want to trigger new updates in the store, yes?

If so, I would take the data out of the store to make those modifications:

var freezer = new Freezer({ data: {a:1} });

freezer.on('update', function(){
  console.log('updated');
});

//Let's modify the data to use it
var data = freezer.get().data.toJS();

//Now you can modify the variable
//no update triggered
data.b = 2;
data.c = [1,2,3];

// Whenever you want to set the data use reset
// and that will trigger the update
// but nodes inside previous data object won't 
// be preserved
// freezer.get().data.reset( data );
// or freezer.get().set({data: data});

http://jsbin.com/qisozo/4/edit?js,console

arqex commented 9 years ago

As for the same node shared in two different stores, that's the expected behavior. Freezer is thought to be the single source of truth for some domain in your UI, so sharing nodes among stores is not supported.

If you need to share some data for two parts of your app, just make those parts share the store. I mean, instead of having two stores, it is better to have one with all the data and pass a node to the component instead of passing the full store.

grassick commented 9 years ago

Thanks!