[SOLVED] Reactive Publish & Subscribe Not Working in React

I seem to be having some trouble with Meteor’s reactivity.

I have created a publication for my database:

Meteor.publish('menus', function publishMenus() {
  return MenuCollection.find({ userId: this.userId });
});

I am then subscribing to this and printing the results in my React component:

export const MenuList = () => {
  // Wait for menu subscription data
  const [menus, isLoading] = useTracker(() => {
    const sub = Meteor.subscribe('menus');
    const menus = MenuCollection.find();
    return [menus, !sub.ready()]
  });

  return (
    <ul className="list-group">
      {isLoading ? 'Loading...' :
        menus.map(menu => {
          return <MenuListItem key={menu._id} menu={menu} />
        }
      )}
    </ul>
  );
};

A parent component contains a form to add items to the collection. This parent component also then renders this child list component:

export const Menu = () => {
  // Handle form submit - inset menu into db
  const submit = data => {
    Meteor.call('menus.insert', data, err => {
      if (err) console.log(err);
    });
  };

  return (
    <>
      <AutoForm schema={menuBridge} onSubmit={submit} />

      <hr/>

      <MenuList />
    </>
  );
};

However, I have to refresh the page for the newly added item to render in the list. I have the Meteor developer tools Chrome extension installed and the record updates and appears there - in the Minimongo section without having to refresh it.

I thought Meteor automatically handled this reactivity but some articles / answers suggest it doesn’t and that I need to use a package like meteor-reactive-publish or meteor-publish-composite? Other answers suggest the opposite - that I do not need an extra package and that Meteor should handle this.

What am I missing? Any help would be greatly appreciated :slight_smile:

I believe the problem is in this line:

const menus = MenuCollection.find();

Collection#find returns a cursor, and this is what you expose from within useTracker. Cursor#map is therefore called outside a reactive computation, and that’s why the reactivity doesn’t kick in:

Cursors are a reactive data source. On the client, the first time you retrieve a cursor’s documents with fetch , map , or forEach inside a reactive computation (eg, a template or autorun ), Meteor will register a dependency on the underlying data. Any change to the collection that changes the documents in a cursor will trigger a recomputation.

@see https://docs.meteor.com/api/collections.html#Mongo-Collection-find

So if that’s the reason, you can fix it by changing to above line as follows:

const menus = MenuCollection.find().fetch();

@peterfkruger You’ve saved me again! Thank you so much! That has worked :slight_smile:

I thought I had already tried that for some reason.

1 Like

Forgetting .fetch() is so easy, that I got this gotcha around a dozen times until I finally learned it :slight_smile:

1 Like