ManuelDeLeon / viewmodel-react

Create your React components with view models.
MIT License
24 stars 3 forks source link

Meteor subscriptions and autorun #38

Closed davidsavoie1 closed 6 years ago

davidsavoie1 commented 6 years ago

Hi Manuel,

I have problems handling Meteor subscriptions with ViewModel. It's hard to present a very simple example since it implies Client/Server communication, but here's a small video illustrating my issue.

  1. User is logged in and sees the data
  2. User logs out, then in again and sees a loading icon indicating that the subscription is not ready
  3. Îf page is refreshed, the subscription becomes ready

Here's the component subscribing to the data:

ProjectsPanel({
  render() {
    return (
      <div>
        <h1 class="ui header">Projets</h1>
        <div b="class: loadingDimmerClass">
          <div class="ui medium text loader">Loading</div>
        </div>
        <WorkspaceGroup b="repeat: workspaces, key: _id" />
      </div>
    );
  },

  subReady: false,

  autorun: () => Meteor.subscribe('workspaces.all', () => this.subReady(true)),

  loadingDimmerClass: () => (this.subReady()
    ? 'ui disabled inverted dimmer'
    : 'ui active inverted dimmer'
  ),
  workspaces: () => Workspaces.find({}, { fields: { title: 1 } }), // Mongo collection
});

I put the subscription inside an autorun so that it gets destroyed with the component, but I get the same behaviour if I use the created VM property instead. However, if I put it in the rendered prop, it seems to work fine, but I think I would have to stop the subscription on component destroyed.

Why is it that it works when I refresh the page, but not when app state changes (user logs in), which creates the component?

What is the correct way to handle subscriptions with ViewModel-React and Meteor?

davidsavoie1 commented 6 years ago

It seems to be related to this issue, for which I created a reproduction repo.

davidsavoie1 commented 6 years ago

For the record, I think I know what was wrong with my own code (yup, this one is clearly on me!). I guess that the subscription was never getting a ready state only because the component was loaded too early, before the user info could be received from the server. The user might have then still be considered logged-out. As the Meteor docs mention:

In the case of a logged-out user, we explicitly call this.ready(), which indicates to the subscription that we’ve sent all the data we are initially going to send (in this case none). It’s important to know that if you don’t return a cursor from the publication or call this.ready(), the user’s subscription will never become ready, and they will likely see a loading state forever.

When I refreshed the page, the race condition might have ended differently then on login... Ah, so many moving parts! 😄