2

i'm abstracting my firebase api calls out of my react native components to a service layer. This is working well for calls that return a promise, but this one call, onAuthStateChanged, doesn't return a promise. Without the service layer i would just do:

firebase.auth().onAuthStateChanged(user => {
if (user) {
   //logged in 
} else { //not logged in  }

Now i want to put everything in my services/api.js, i tried several things already but the latest was this:

    export const userLoggedin = () => {
    firebase.auth().onAuthStateChanged(user => {
        if (user) {
            return true
        } else {
            return false
        }
    })
}

Then in my app.js i want to check if userLoggedin returns true or false, so i can navigate the user depending if he is already logged in.

    if (userLoggedin()) {
    // logged in 
} else {
    // logged out
}

now this last part is always going to be in the else, because the userLoggedin return true way later, and it doesn't wait for it. What is a good solution for this problem?

1
  • You need to return the promise and use it. Commented Mar 16, 2018 at 17:32

2 Answers 2

2

You can create a promise around a call that otherwise doesn't support promises:

export const userLoggedIn = () => {
  return new Promise((resolve, reject) => {
    firebase.auth().onAuthStateChanged(user => {
      resolve(!!user);
    })
  });
}
Sign up to request clarification or add additional context in comments.

2 Comments

This is the first time i see this double ! operator. Had to lookup what it means. tnx for teaching me something new! Your answer worked for me. But the one provide by kingdaro is a bit more sufficient for my react-native project, so i am going to mark his answer as correct. But yours also helped me!
Ah, sorry about the !!, i use it enough that i forget it can be confusing. Anyway, as you've likely discovered it's used to convert it into a boolean
1

Though a promise works for a one-off check, the auth state has the potential to change multiple times over your application's lifetime, if the user decides to sign out later, for example. That, and your app might actually render before firebase is done checking whether the user is logged in, resulting in a false auth check even if they're actually logged in.

Here's my suggested approach: use component state to keep track of the current user, as well as whether the auth state is currently being checked.

Note: this is reliable because the onAuthStateChanged callback will always fire at least once. If the auth check finishes first, the callback will get called immediately after it's attached.

import React from 'react'
import firebase from 'firebase'

class App extends React.Component {
  state = {
    authenticating: true,
    user: null,
  }

  componentDidMount() {
    firebase.auth().onAuthStateChanged(user => {
      this.setState({ user, authenticating: false })
    })
  }

  render() {
    if (this.state.authenticating) {
      // we're still checking the auth state,
      // maybe render a "Logging in..." loading screen here for example
      return <LoadingScreen />
    }

    if (this.state.user === null) {
      // not logged in
      return <Redirect to='login' />
    }

    // here, auth check is finished and user is logged in, render normally
    return <AppContent />
  }
}

Also, side note, if you ever do just want to check somewhere in your app whether or not the user is currently logged in, firebase.auth().currentUser !== null works as well.

1 Comment

Tnx this is a great solution! managed to get it to work in combination with nicholas towers answer. Also thanks for reminding me about the currentUser call, totally forgot about that one!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.