React-native-firebase: How can I reliably attach my own data to firebase.auth().currentUser?

Created on 27 Feb 2019  路  3Comments  路  Source: invertase/react-native-firebase

I am using Firebase for auth, but my user profiles are actually stored in PostgreSQL. I am using the Firebase uid to tie them together.

On app start before I show the user any screens, I have something like:

firebase.auth().onAuthStateChanged(async user => {
  firebase.auth().currentUser.profile = await fetchProfile(user.uid);
})

So after autologin happens, I can refer to firebase.auth().currentUser.profile throughout my app to get the parts that I need, such as the uid => id in my database.

This works about 90% of the time, but I noticed there is the rare occasion that firebase.auth().currentUser gets reset and loses .profile, and therefore it bugs out. I am not sure why this happens, because it's not like onAuthStateChanged happens more than once. It is arbitrary and I am unable to reproduce it consistently.

Is there a reliable way I can attach this profile object to firebase.auth().currentUser so it doesn't get lost, or are there any other recommendations on how to keep track of the current (PostgreSQL) profile throughout the app for the current session (until the user closes the app)?

Most helpful comment

Unfortunately, this isn't a recommended way of doing this as you're mutating something which is out of your control, and like you've seen this can be changed by the internals of RNFB.

The recommended approach would be to store this data in a store where you have control, for example using Context:

import React, { useState, useEffect, createContext } from 'react';
// ...

const UserContext = createContext();

// example, prefer useReducer
function App() {
  const [loading, setLoading] = useState(true);
  const [user, setUser] = useState();
  const [profile, setProfile] = useState();

  useEffect(() => {
    const unsub = firebase.auth().onAuthStateChanged(async user => {
      setProfile(await fetchProfile(user.uid));
      setUser(user);
      setLoading(false);
    });

    return unsub;
  }, []);

  if (loading) {
    return null;
  }

  // Logged out
  if (!user) {
    return null;
  }

  return (
    <UserContext.Provider
      value={{
        user,
        profile,
      }}
    >
      {/* ... */}
    </UserContext.Provider>
  );
}

All 3 comments

Unfortunately, this isn't a recommended way of doing this as you're mutating something which is out of your control, and like you've seen this can be changed by the internals of RNFB.

The recommended approach would be to store this data in a store where you have control, for example using Context:

import React, { useState, useEffect, createContext } from 'react';
// ...

const UserContext = createContext();

// example, prefer useReducer
function App() {
  const [loading, setLoading] = useState(true);
  const [user, setUser] = useState();
  const [profile, setProfile] = useState();

  useEffect(() => {
    const unsub = firebase.auth().onAuthStateChanged(async user => {
      setProfile(await fetchProfile(user.uid));
      setUser(user);
      setLoading(false);
    });

    return unsub;
  }, []);

  if (loading) {
    return null;
  }

  // Logged out
  if (!user) {
    return null;
  }

  return (
    <UserContext.Provider
      value={{
        user,
        profile,
      }}
    >
      {/* ... */}
    </UserContext.Provider>
  );
}

Heh, I figured there was something off about how I was doing it because it felt janky! I really appreciate the detailed response and idea. I'm actually using Apollo Client, which already wraps my app in a Provider, so the current profile should be in that store already after fetchProfile, so I guess I could just use that. I'll see if that's 100% reliable (assuming it should be as I have good control over it) and if not, could always just implement context as you demonstrated. Thank you so much for your insight!

UPDATE: Yep, using another store (in my case I just used my Apollo Client store), works wonderfully.

No problem. This question comes up a bit when using Firebase... The general response would be to make use of Firestore/RTB if possible, by storing the data within /users/:id and subscribing to that.

Other options are also running a Cloud Function which uses the Admin SDK to handle changes server side and push to the data to wherever you want. Lots of options but appreciate you're working with another data store 馃憤

Was this page helpful?
0 / 5 - 0 ratings