useSubscribe and useTracker fire multiple times `react-meteor-data`

Does anyone know why useSubscribe / useTracker from the react-meteor-data package fires so many times in quick succession. Is that by design? If so, what is the benefit? If not, is that a known bug?

This is straight from the Meteor example:

// will fire multiple times
const isLoading = useSubscribe('links');

If I do something like this instead, it works as I’d expect:

useEffect(() => {
  const computation = Tracker.autorun(() => {
    Meteor.subscribe('links');
  });

  return () => computation.stop();
}, []);

I don’t use React or have any plans to but I noticed this when working on one of my packages and it’d be nice to get things to play nicely.

2 Likes

How about you replace const isLoading = ... with a console.log(‘firing’). It the console log fires as often as you observe in your problem, this has nothing to do with useSubscribe / useTracker. They fire as many times they are called. It is up to you to make the rules.
The difference in your 2 examples is that in the first the function triggers every time the component re-renders while in the second example, the function only runs on the first load and it will run again when the component mounts again not when the component re-renders due to state or props updates.

Could the issue you mentioned be similar to the one in this GitHub issue? A fix was recently provided: Test for concurrency issue with use find client by PedroMarianoAlmeida · Pull Request #428 · meteor/react-packages · GitHub. It hasn’t been deployed yet, but will be included in the next beta.

For useSubscribe, a similar case was found and fixed here: fix: premature cleanup in strict mode and improved performance by welkinwong · Pull Request #429 · meteor/react-packages · GitHub. The contributor added tests, so it will also be part of the next beta after a quick check.

If your issues are different, it would help to reproduce them in a test case in the react-packages repo for a future fix.

1 Like

Some more info for those curious and a quick reproduction.

Here’s the example straight from the Meteor 3 + React repo but with pieces commented out which seem to trigger additional renders and invocations of Meteor.subscribe.

export const Info = () => {
  const isLoading = useSubscribe('links');
  // const links = useFind(() => LinksCollection.find());
  const links = [];

  console.log('info')

  /* if (isLoading()) {
    return <div>Loading...</div>;
  } */

  return (
    <div>
      <h2>Learn Meteor!</h2>
      <ul>{links.map(
        link => <li key={link._id}>
          <a href={link.url} target="_blank">{link.title}</a>
        </li>
      )}</ul>
    </div>
  );
};
// in client/main.jsx
const originalSubscribe = Meteor.subscribe;
Meteor.subscribe = function(name, ...args) {
  console.log('subscribe invoked')
  return originalSubscribe.call(this, name, ...args);
}

In the browser console:

Meteor.subscribe is invoked twice. I don’t see that behavior in other front ends or with a simple usage of useEffect + Tracker.autorun.

Beyond that, it gets worse if you uncomment isLoading and useFind. Is this expected behavior?

1 Like