AdamBrodzinski / meteor-flux-leaderboard

Flux Example with React & Meteor
131 stars 19 forks source link

Use Collection.observe Instead? #2

Closed AdamBrodzinski closed 9 years ago

AdamBrodzinski commented 9 years ago

I don't think it makes much of a difference but this might be easier to understand:

Players.find({}).observe({
  changed: (newDocs) => {
    CollectionActions.playersChanged(newDocs);
  }
});

http://stackoverflow.com/questions/25999324/meteor-deps-autorun-vs-collection-observe

chenkaiC4 commented 9 years ago

@AdamBrodzinski e..., it's easier to understand, but catch a error, but Interesting ^_^

Exception in queued task: Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.

the error inferred to a issue, maybe my guess:

dep.autorun run computation block after mini-mongo changed, BUT, Collection.observe invoke callbacks before mini-mongo changed.

I have looked up the official document, but it doesn't give a clear noticement.

I use follow code to test:

if (Meteor.isClient) {
  Players.find({}).observe({
    changed(docs) {
      console.log('\n[observeChanges]');
      console.log(docs);
      // follow code will cause error: Cannot dispatch in the middle of a dispatch.
      //CollectionActions.playersChanged(Player.findLeaders());
    },
  });
  // watch collections on Minimongo cache and trigger action on change
  Meteor.startup(() => {
    Tracker.autorun(computation => {
      var docs = Player.findLeaders();  //Players.find({}).fetch();
      if (computation.firstRun) return; // ignore first empty run
      console.log('\n[Tracker] collection changed');
      this.CollectionActions.playersChanged(docs);
    });
  });
}

after select one play, and click 'Add 5 Points' bottom, the console is:

[PlayerActions] INCREMENT_SCORE K69oYBbmDokx9WnNb
[observeChanges]
Object {name: "Ada Lovelace", score: 55, _id: "K69oYBbmDokx9WnNb"}
[PlayerStore] incrementScore
[App] rendering 
[Tracker] collection changed
[CollectionActions] PLAYERS_CHANGED [Object, Object, Object, Object, Object, Object]
[PlayerStore] updating state
[App] rendering
// in PlayStore
onIncrementScore(docId) {
    //Player.incrementScore() could use a fat model also
    Players.update({_id: docId}, {$inc: {score: 5}});
    // no setState required since tracker will fire a change
    console.log('[PlayerStore] incrementScore');
  }

Execution order like this: PlayerActions.incrementScore --> dispatch to PlayerStore.onIncrementScore --> update mini-mongo(insecure mode cause sync server mongo data) --> trigger mongo observer(dispatch not end can't create another dispatch) --> now console in PlayStore.onIncrementScore dispatch end --> Track.autoRun --> ......

it's amaze when PlayStore.onIncrementScore update the mongo, the console statement follow update statement doesn't executed until cursor observer finished. seem like cousor observer has a higher priority. And if we invoke CollectionActions.playersChanged(Player.findLeaders()) in observe, the playersChanged action will be trigger with a new dispatch and cause error above. But Track.autoRun doesn't cause the error. I think it's because the dispatch created by PlayerActions.incrementScore has finished, and we can dispatch again.

omg, writing make me crazy, wish those words not crazy you, @AdamBrodzinski. Hope to have more discuss about this question.

AdamBrodzinski commented 9 years ago

Hmmm interesting. I think the dispatch error is because both tracker and observe are dispatching at the same time? (this is usually not allowed in flux to prev. cascading changes). I'm thinking if you comment out the tracker all together than it would eliminate the dispatch error? I'm not too sure about the order.

AdamBrodzinski commented 9 years ago

Yep this won't work for our flux use case. If you're making an update, it triggers but for example if you are watching for changes on the logged in user, you won't get an update when they login.

Closing.