Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meteor ReactiveDict doesn't trigger event on first insert

I migrated a piece of code from Session to ReactiveDict. After some debugging it appears ReactiveDict is not triggering any recomputation event when the { key : value } pair is added to the dictionary.

var selected = new ReactiveDict()
  Meteor.autorun(function() {
    for (var k in selected.keys)
      console.log("reactiveDict", k, " : ", selected.get(k))
})

Meteor.setTimeout(function () { selected.set('test', true) }, 1000)
Meteor.setTimeout(function () { selected.set('test', false) }, 2000)
Meteor.setTimeout(function () { selected.set('test', true) }, 3000)
Meteor.setTimeout(function () { selected.set('test', false) }, 4000)

That code doesn't print anything whereas the following works

Meteor.autorun(function() {
    for (var k in Session.keys)
        console.log("session", k, Session.get(k))
})

Session.set('test', true)

It prints "session test true" (Session is saved across client updates, therefore one needs to make sure to start with a fresh environment for the test to be valid)

My understanding was that ReactiveDict was a drop-in replacement of Session (actually that it was the code of Session made available into a package). But in this case they behave very differently.

The only workaround I found is to force the react trigger with an artificial variable that already exists

var selected2 = new ReactiveDict()
selected2.set('workaround', false)

Meteor.autorun(function() {
  for (var k in selected2.keys)
  {
    var v = selected2.get(k)
    if (k != "workaround") console.log("selected2", k, " : ", v)
  }
})

var selected2_set = function (key, value) {
  var w = (selected2.keys[key] == undefined)
  selected2.set(key, value)
  if (w) {
    selected2.set('workaround', true)
    selected2.set('workaround', false)
  }
}

Meteor.setTimeout(function () { selected2_set('test', true) }, 1000)
Meteor.setTimeout(function () { selected2_set('test', false) }, 2000)
Meteor.setTimeout(function () { selected2_set('test', true) }, 3000)

Prints "selected2 test : true", then false, then true

My questions are

  • is the observed behavior of ReactiveDict a feature or a bug ?
  • is there a better workaround ?

The only "improvement" of the workaround that comes to my mind is allowing 'workaround' to alternate between true and false, but that creates other types of issues like not being able to iterate just looking for true values.

Also, one has to be careful not to filter 'workaround' out because then the recomputation is not properly triggered. For instance the following change in the autorun doesn't work because get('workaround') never gets ran.

if (k != "workaround") console.log("selected2", k, " : ", selected2.get(k))
like image 673
Diego Olivier Fernandez Pons Avatar asked May 07 '15 02:05

Diego Olivier Fernandez Pons


1 Answers

keys is simply a property of the ReactiveDict and does not register a reactive dependency so this behavior is expected. In the current implementation (meteor v1.1) there isn't an easy way to achieve what you are looking for.

In an upcoming version, all will set up a reactive dependency on all of the keys, so this will work:

Tracker.autorun(function() {
  _.each(selected.all(), function(value, key) {
    console.log(key + ":" + value);
  });
});

If you want to use that version now, you can download the source to a file called lib/reactive-dict2.js and replace any reference to ReactiveDict with ReactiveDict2 (currently lines 19 and 43 - note this is critical or other parts of your app may break). Then you can do this to use it:

var selected2 = new ReactiveDict2()

And the above code should work.

like image 150
David Weldon Avatar answered Oct 30 '22 01:10

David Weldon