Cursor.observeChanges() in React

I’ve hit an issue while porting my app from Blaze to React.

I have a notifications system that triggers HTML5 notifications and sounds when new documents are added to a collection. A simple example is a chat box that notifies when there is a new item. This was easy enough in Blaze:

Template.chatBox.onCreated(function() {
	self.notifications = Chat.find().observeChanges({
		added: function(id, doc) {
			if (self.ready.get() && doc.user) {
				notify(displayname(doc.user), doc.detail, profilePicture(doc.user, 250));
				playSound('chat');
			}
		}
	});
});

Template.chatBox.onDestroyed(function() {
	this.notifications.stop();
});

But now with React I’m finding it impossible to use this same setup. I realise that to use reactive observers they must be used in getMeteorData() inside a react component. However due to how getMeteorData() works by re-running completely whenever there is a change to the reactive data, the notifications observer is recreated and all ‘added’ callbacks for documents that are already there are accordingly recalled. This means whenever a new document is added I’ll get a cacophony of notifications for all of the 20 documents that are already there.

I’ve tried wrapping that data in child component using props and componentWillUpdate, but since it gives me the whole array of items I can’t effectively see is there is a new item. Additionally, since I’m limiting the publication to 20 documents, componentWillUpdate runs twice - first with 21 documents and then with 20 as miniMongo is synced with the server.

Any ideas around how to effectively create a simple, reactive collection observer using React?

Why not a combination of Tracker.autorun and observeChanges?

Thanks! I’ve finally worked this out thanks in part to your suggestion. I originally tried to run the Tracker.autorun inside of componentDidMount, but it wasn’t reactive in there due to this bug.

However following a workaround suggested by stuballo, I got it working. Here’s the code for anyone interested:

ChatBox = React.createClass({
  mixins: [ReactMeteorData],

  componentDidMount() {
    setTimeout(this.startComputation, 0);
  },

  startComputation() {
    this.tracker = Tracker.autorun(() => {
      Chats.find({lobbyId: this.props.lobbyId}).observeChanges({
        added: (id, doc) => {
          if (this.data.subsReady && doc.user) {
            playSound('chat');
          }
        }
      });
    });
  },

  componentWillUnmount() {
    this.tracker.stop();
  }
});
1 Like