DesignmanIO/react-native-meteor-offline

User object is null in offline mode

Closed this issue · 19 comments

When offline, the only possibility to get user object is with: MeteorStore.getState().
When trying to reach it like another collection in the properties of a component, it is 'null'.

Try with 1.1.13. It shouldn't be null unless you're not signed in.

Ok I'll test right now.
Very quick :)

No, it is still null.
When I disconnect the server ('Disconnected from DDP server.'), the user is still detected. But as soon as I refresh the react native app, user is null.
It happens only with the 'users' collection.

Did you try in the example, or do you have a repo that I can test? It works for me in the example (if you go to "Profile").

BTW, in case you don't already know about it, there's a preference pane for osx called "Link Conditioner" that allows you to quickly test network connectivity, without jumping on and off wifi.

No I didn't try in the example, but I will in 7 hours when I'm back home.
I also have a repo, I will give it to you but I need to commit tonight.

Oh I didn't know this spec, perfect cheers for the info !

I just tested with the example and it doesn't work (with the version 1.1.13).
The user is not persisted offline (still null).

I have added console.log(user); line 28 in index.js file (RNApp).
Then disconnected the meteor server.
Then refreshed the RNApp.

[FIXED]
I don't think this is a good pattern, but I have replaced user: Meteor.user(), by user: Meteor.collection('users').findOne() in createContainer and the user object is persisted offline.

I'll take a look, but in the meantime that's not a bad solution. react-native-meteor does that on the backend, I believe.

Ok I think I got it.
Actually the problem is not your package but the react-native-meteorone (User.js file).

When you do a Meteor.user() if it doesn't find a this._userIdSaved, it returns null.
if it finds it, it returns this.collection('users').findOne(this._userIdSaved).

user() {
    if(!this._userIdSaved) return null;

    return this.collection('users').findOne(this._userIdSaved);
  }

This is exactly the same with Meteor.userId():

userId() {
    if(!this._userIdSaved) return null;

    const user = this.collection('users').findOne(this._userIdSaved);
    return user && user._id;
}

The this._userIdSavedis defined when login, maybe we should ask to contributors of this repo to set one more item (result.id at the first login on the app) in the Asyncstorage:

_handleLoginCallback(err, result) {
    if(!err) {//save user id and token
      AsyncStorage.setItem(TOKEN_KEY, result.token);
      Data._tokenIdSaved = result.token;
      this._userIdSaved = result.id;
      Data.notify('onLogin');
    } else {
      Data.notify('onLoginFailure');
      this.handleLogout();
    }
    Data.notify('change');
  },

That makes sense. So, the _handleLoginCallback should be fine, it's the _loginWithToken that calls it in a callback from a Meteor.call, which then requires a connection. Probably best would be to call _handleLoginCallback from initMeteorRedux. Thoughts?

Yeah I think you're right, calling _handleLoginCallback() would be the best option.

Huh, this is tricky... The userId is obtained from the Meteor.call, not from AsyncStorage, so I can't pass the required parameters. Ideas?

Yeah a bit complicated ...
Why not just create 2 functions in your package for now like:

user() {
    return Meteor.collection('users').findOne();
  }

and

userId() {
    const user = Meteor.collection('users').findOne();
    return user && user._id;
}

It will avoid going through this._userIdSaved.
It won't make us in trouble because there will be only one user using the app (normally).

And then asking for react-native-meteor contributors to register this._userIdSaved in the asyncstorage ...

If you have more than one user, is that guaranteed to return the logged in user?

With autopublish package I don't know. (I think it should work)
But if you remove it, then you just have to create a custom publish for user data:

Meteor.publish("userData", function () {
  if (this.userId) {
    return Meteor.users.find({_id: this.userId});
  } else {
  this.ready();
  }
});

In fact I think it works in every cases, even without the above snippet

But I mean if you have multiple users, how do you know the user you get back is the logged in one? You could have 100 users and it could return any random one, couldn't it?

You have the const TOKEN_KEY = 'reactnativemeteor_usertoken'; that is stored on the Asyncstorage. So it is linked to a unique userId.

Then, once you're connected you will get info of the user connected with the token, even if there are 100 other users on the database

I added MO.user() in v2.2.0