apollographql / meteor-integration

🚀 meteor add apollo
http://dev.apollodata.com/core/meteor.html
108 stars 45 forks source link

Handling log in/log out #52

Closed SachaG closed 7 years ago

SachaG commented 8 years ago

I currently have a currentUser query in my global app container that loads the current user, and I'm handling user accounts through the accounts-ui package.

The problem is that the Apollo query doesn't know it needs to re-run when the Meteor user's logged in/logged out state changes.

So the flow looks something like this:

  1. Load app, run query for currentUser which returns nothing because no user is logged in.
  2. User logs in.
  3. ???
  4. Rerun query for currentUser to get the current user profile.

As you can see things break down at step 3. What's the best way of telling the query to rerun? One solution would be calling refetch() in an autorun block containing Meteor.user()?

Another issue related to the way I've currently set things up is that refetching the currentUser query would force my entire app to reload:

const AppContainer = (props, context) => {

  const {loading, refetch, currentUser, categories} = props.data;

  return <Telescope.components.App
    ready={!loading}
    currentUser={currentUser}
    categories={categories}
    events={Events}
    {...props}
  />;
};

Is there a way to rerun the query without setting loading to false? Or a better pattern for setting up your app container?

Siyfion commented 8 years ago

As myself and @SachaG had discussed on Slack, the other option might be to setup an Apollo subscription that notifies if the logged-in user changes. Obviously this involves adding hooks into all the server-side log in/out functions.

lorensr commented 8 years ago

If Meteor.user() is already being updated by Meteor pubsub, you could use that instead of currentUser query?

If you keep the query, using an apollo sub seems more efficient than the refetch autorun, but either would work.

Either way, can deal with not rendering on user doc changes with shouldComponentUpdate? Not familiar with loading, but could make an loadingInitialData flips-one-way var out of it. Starting:

loading = true
loadingInitialData = true

and on every change:

if (!loading)
  loadingInitialData = false
SachaG commented 8 years ago

OK, so here's what I get using Meteor.user() (let's call it "Strategy A"). It's not ideal in the sense that it's still relying on DDP, but it seems to work.

In this example, let's say I want my global app container to wait for both the current user and a list of categories (loaded via Apollo) to load before loading the rest of my app:

import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import { composeWithTracker } from 'react-komposer';

// first HoC that gets user loaded via DDP
const currentUserComposer = (props, onData) => {
  onData(null, {currentUser: Meteor.user()});
}

// second HoC that gets Apollo data
const AppContainer = props => {  
  return <Telescope.components.App
    ready={!props.data.loading} // from Apollo
    categories={props.data.categories} // from Apollo
    currentUser={props.currentUser} // from currentUserComposer
    {...props} // pass on other props (router, etc.)
  />;
};

const AppContainerWithData = graphql(gql`
  query getAppData {
    categories {
      _id
      name
    }
  }
`)(AppContainer);

module.exports = composeWithTracker(currentUserComposer)(AppContainerWithData);
lorensr commented 8 years ago

Looks good. And then in Telescope.components.App, you could do

render() {
  const userDataIsLoading = Meteor.userId() && !props.currentUser
  if (!props.ready || userDataIsLoading) {
    return <Loading />
  }
  ...
}

Or make a loading prop inside currentUserComposer that took into account both props.ready and userDataIsLoading

tmeasday commented 8 years ago

I think the generally recommended strategy is to call resetStore() when the user changes, is that right @helfer?

SachaG commented 8 years ago

Where would you call it from? Autorun block with Meteor.user() inside it? Also, where is resetStore documented? Would it reset the entire store or is it ok to just rerun one specific query?

tmeasday commented 8 years ago

It re-runs all queries. I guess in an autorun block makes sense?

tmeasday commented 8 years ago

http://dev.apollodata.com/core/apollo-client-api.html#ApolloClient\.resetStore (not really documented per-se ;) )

SachaG commented 8 years ago

What if you only wanted to rerun one specific query?

tmeasday commented 8 years ago

I guess you could just call refetch on it?

helfer commented 8 years ago

@tmeasday yep, that's all correct! I just fixed resetStore, so it should actually work now 🙂

xavxyz commented 8 years ago

Thanks @helfer, client.resetStore() works well! We use it with login/logout hooks of std:accounts-ui 🚀

xavxyz commented 7 years ago

Closing as it's now documented and working well 👌

➡️ http://dev.apollodata.com/core/meteor.html#Accounts

acomito commented 7 years ago

@SachaG are you using meteor on the client for accounts, or are you doing a all graphql implementation similar to meteor-apollo-accounts?

SachaG commented 7 years ago

We're using Meteor for the accounts with DDP, but yeah ideally we would have a graphql implementation…