Yusuff Faruq Frontend web developer and anime lover from Nigeria.

Handling user authentication with Firebase in your React apps

14 min read 3991

Handling User Authentication With Firebase In Your React Apps

Sometimes, while building an application, we want each user to have a personalized experience. This is made possible through user authentication, whereby a user logs in to the application to access personalized information such as a social media feed or use a personalized service.

In this article, we will which is a backend-as-a-service (BaaS) that allows both web and mobile developers to perform common backend tasks with no need for maintenance or upkeep.

At the end of this article, we will have built a simple React app that logs users in and displays their username, email, and profile picture. You can find a demo of the application here.

Prerequisites

This article is primarily targeted towards intermediate-level React developers. However, no matter your skill level, you can still follow along well if you have an understanding of routing, the Context API, and other concepts that will be used in this article.

We will be using Tailwind CSS to style our components. Tailwind CSS is a utility-first CSS framework for rapidly building custom designs. It is very similar to Bootstrap, so it’s easy to pick up. You do not need to have a solid understanding of Tailwind CSS to follow along with this article.

Setting up

Before we begin coding, we need to set up some things for our app to work. Firstly, we need to create a new React app. You can easily do that using create-react-app:

npx create-react-app firebase-auth-app

Now that we have done that, we need to set up Tailwind CSS to work with React. Here’s a great guide that helps you get Tailwind CSS up and running with create-react-app in minutes.

Next, we need to install Firebase:

npm install --save firebase

With that done, we can now set up Firebase.

Setting up Firebase

Firstly, we need to create a new file dedicated to Firebase. Create a src folder in the root directory, and in it, create a file called firebase.js.

We made a custom demo for .
No really. Click here to check it out.

Now, go to the Firebase website and click on the Get started button. You will be taken to a page where you can create a new project. Once you are done, you should be taken to a dashboard page similar to the image below.

The Firebase Get Started Page

We will be using two Firebase services for this project: the Authentication service and Cloud Firestore service. Let’s set up Cloud Firestore first.

Select the Database option from the side menu. You should now be able to choose either of the two database services that Firebase provides: Cloud Firestore or the Realtime Database. In this article, we will use Cloud Firestore.

Cloud Firestore Page

Now create a Cloud Firestore database. You should now see a modal for setting the security rules of your database. Choose Start in test mode. You should have an empty database ready for use. It should look like this:

Empty Database Page

Now that we have our database ready, let’s set up authentication. From the side menu, select the Authentication option. Now, select the Sign-in method tab. You should have something like this:

Sign-in Method Page

Here, you can set up authentication for different providers such as Google, Facebook, GitHub, and so on. In our app, we want to enable Google authentication and email/password authentication. Let’s set up Google authentication first. Click on the Google option.

Google Authentication Setup Page

You can now enable Google authentication by toggling the button at the upper right-hand side. You also need to provide a project support email. Once that is done, save the changes and do the same with the email/password option.

Now that Cloud Firestore and authentication have been set up, we need to get our project’s configuration details. This is needed to link our code to our Firebase project.

To get our Firebase configuration details, go back to the project overview page and add a web app to the Firebase project. After registering the app, you should get the configuration details in the form of a JavaScript object.

const firebaseConfig = {
  apiKey: 'AIzaXXXXXXXXXXXXXXXXXXXXXXX',
  authDomain: 'test-XXXX.firebaseapp.com',
  databaseURL: 'https://test-XXXXXX.firebaseio.com',
  projectId: 'test-XXXX',
  storageBucket: 'test-XXXX.appspot.com',
  messagingSenderId: 'XXXXXXX',
  appId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
};

Now open your firebase.js file and import Firebase, Cloud Firestore, and Firebase’s Authentication service:

import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";

const firebaseConfig = {
  apiKey: 'AIzaXXXXXXXXXXXXXXXXXXXXXXX',
  authDomain: 'test-XXXX.firebaseapp.com',
  databaseURL: 'https://test-XXXXXX.firebaseio.com',
  projectId: 'test-XXXX',
  storageBucket: 'test-XXXX.appspot.com',
  messagingSenderId: 'XXXXXXX',
  appId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
};

Now we just need to initialize our Firebase app using the configuration details we got from Firebase. Once that is done, we need to get references to Cloud Firestore and the Authentication service and export them.

firebase.initializeApp(firebaseConfig);
export const auth = firebase.auth();
export const firestore = firebase.firestore();

Building our components

Now that Firebase is fully set up, we can start building our components. Our application will cover:

  • Signing in and signing out
  • Signing up with Google or email/password
  • Password reset
  • A profile page

Therefore, we will build five components:

  • An Application component, which will render either the sign-in/sign-up routes or the profile page, depending on whether the user has been signed into the application
  • A PasswordReset component, which allows the user to reset their password in the event they lose or forget it
  • A ProfilePage component, which will display the user’s display name, email, and profile picture or a placeholder image if they are signing in via email/password
  • A SignIn component for logging the user in to the application
  • A SignUp component, which allows new users to sign up to use the application

Reach Router will be used to route between the sign-in and sign-up routes or pages. Reach Router is an accessibility-focused routing library for React. It is very easy to get started with and is a perfect fit for our application.

Now that we know what we want to do, let’s start writing some code!

In your src folder, create a new folder called Components. This is where our components will reside. Let’s start with the Application component.

The Application renders the ProfilePage component if the user has logged in and a Router component (which comes from Reach Router) housing the SignUp, SignIn, and PasswordReset components/routes otherwise.

Later on, we will use React’s Context API to pass the current user to all the components that need it. For now, we will just create a placeholder variable to represent the current user.

In case you are not familiar with Reach Router, the Router component is used to house the different routes we want to route between. Each immediate child of the Router component must have a path prop, which acts as a link to that route.

import React from "react";
import { Router } from "@reach/router";
import SignIn from "./SignIn";
import SignUp from "./SignUp";
import ProfilePage from "./ProfilePage";
import PasswordReset from "./PasswordReset";
function Application() {
  const user = null;
  return (
        user ?
        <ProfilePage />
      :
        <Router>
          <SignUp path="signUp" />
          <SignIn path="/" />
          <PasswordReset path = "passwordReset" />
        </Router>

  );
}
export default Application;

Now, let’s build our SignIn component. The code for that is as follows:

import React, {useState} from "react";
import { Link } from "@reach/router";

const SignIn = () => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [error, setError] = useState(null);
    const signInWithEmailAndPasswordHandler = 
            (event,email, password) => {
                event.preventDefault();
    };

      const onChangeHandler = (event) => {
          const {name, value} = event.currentTarget;

          if(name === 'userEmail') {
              setEmail(value);
          }
          else if(name === 'userPassword'){
            setPassword(value);
          }
      };

  return (
    <div className="mt-8">
      <h1 className="text-3xl mb-2 text-center font-bold">Sign In</h1>
      <div className="border border-blue-400 mx-auto w-11/12 md:w-2/4 rounded py-8 px-4 md:px-8">
        {error !== null && <div className = "py-4 bg-red-600 w-full text-white text-center mb-3">{error}</div>}
        <form className="">
          <label htmlFor="userEmail" className="block">
            Email:
          </label>
          <input
            type="email"
            className="my-1 p-1 w-full"
            name="userEmail"
            value = {email}
            placeholder="E.g: faruq123@gmail.com"
            id="userEmail"
            onChange = {(event) => onChangeHandler(event)}
          />
          <label htmlFor="userPassword" className="block">
            Password:
          </label>
          <input
            type="password"
            className="mt-1 mb-3 p-1 w-full"
            name="userPassword"
            value = {password}
            placeholder="Your Password"
            id="userPassword"
            onChange = {(event) => onChangeHandler(event)}
          />
          <button className="bg-green-400 hover:bg-green-500 w-full py-2 text-white" onClick = {(event) => {signInWithEmailAndPasswordHandler(event, email, password)}}>
            Sign in
          </button>
        </form>
        <p className="text-center my-3">or</p>
        <button
          className="bg-red-500 hover:bg-red-600 w-full py-2 text-white">
          Sign in with Google
        </button>
        <p className="text-center my-3">
          Don't have an account?{" "}
          <Link to="signUp" className="text-blue-500 hover:text-blue-600">
            Sign up here
          </Link>{" "}
          <br />{" "}
          <Link to = "passwordReset" className="text-blue-500 hover:text-blue-600">
            Forgot Password?
          </Link>
        </p>
      </div>
    </div>
  );
};
export default SignIn;

As you can see, our SignIn component has three pieces of state: email, for storing the user’s email address; password, for storing the user’s password; and error, for displaying error messages in the event of an error during the sign-in process.

Our SignIn component also uses the Link component that Reach Router provides. This component is very similar to the anchor element in HTML, and the to prop of the Link component is similar in function to the href attribute of the anchor element.

Your SignIn component should look like this:

Our SignIn Component

Next, let’s build our SignUp component. The SignUp component is very similar to the SignIn component, and the code is as follows:

import React, { useState } from "react";
import { Link } from "@reach/router";
const SignUp = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [displayName, setDisplayName] = useState("");
  const [error, setError] = useState(null);
  const createUserWithEmailAndPasswordHandler = (event, email, password) => {
    event.preventDefault();
    setEmail("");
    setPassword("");
    setDisplayName("");
  };
  const onChangeHandler = event => {
    const { name, value } = event.currentTarget;
    if (name === "userEmail") {
      setEmail(value);
    } else if (name === "userPassword") {
      setPassword(value);
    } else if (name === "displayName") {
      setDisplayName(value);
    }
  };
  return (
    <div className="mt-8">
      <h1 className="text-3xl mb-2 text-center font-bold">Sign Up</h1>
      <div className="border border-blue-400 mx-auto w-11/12 md:w-2/4 rounded py-8 px-4 md:px-8">
        {error !== null && (
          <div className="py-4 bg-red-600 w-full text-white text-center mb-3">
            {error}
          </div>
        )}
        <form className="">
          <label htmlFor="displayName" className="block">
            Display Name:
          </label>
          <input
            type="text"
            className="my-1 p-1 w-full "
            name="displayName"
            value={displayName}
            placeholder="E.g: Faruq"
            id="displayName"
            onChange={event => onChangeHandler(event)}
          />
          <label htmlFor="userEmail" className="block">
            Email:
          </label>
          <input
            type="email"
            className="my-1 p-1 w-full"
            name="userEmail"
            value={email}
            placeholder="E.g: faruq123@gmail.com"
            id="userEmail"
            onChange={event => onChangeHandler(event)}
          />
          <label htmlFor="userPassword" className="block">
            Password:
          </label>
          <input
            type="password"
            className="mt-1 mb-3 p-1 w-full"
            name="userPassword"
            value={password}
            placeholder="Your Password"
            id="userPassword"
            onChange={event => onChangeHandler(event)}
          />
          <button
            className="bg-green-400 hover:bg-green-500 w-full py-2 text-white"
            onClick={event => {
              createUserWithEmailAndPasswordHandler(event, email, password);
            }}
          >
            Sign up
          </button>
        </form>
        <p className="text-center my-3">or</p>
        <button
          className="bg-red-500 hover:bg-red-600 w-full py-2 text-white"
        >
          Sign In with Google
        </button>
        <p className="text-center my-3">
          Already have an account?{" "}
          <Link to="/" className="text-blue-500 hover:text-blue-600">
            Sign in here
          </Link>
        </p>
      </div>
    </div>
  );
};
export default SignUp;

Our SignUp component should look like this:

Our SignUp Component

Now let’s build our ProfilePage component. We will use placeholder values for the profile picture, email, and display name. The code for the ProfilePage should look like this:

import React from "react";

const ProfilePage = () => {
  return (
    <div className = "mx-auto w-11/12 md:w-2/4 py-8 px-4 md:px-8">
      <div className="flex border flex-col items-center md:flex-row md:items-start border-blue-400 px-3 py-4">
        <div
          style={{
            background:
                `url(https://res.cloudinary.com/dqcsk8rsc/image/upload/v1577268053/avatar-1-bitmoji_upgwhc.png)  no-repeat center center`,
            backgroundSize: "cover",
            height: "200px",
            width: "200px"
          }}
          className="border border-blue-300"
        ></div>
        <div className = "md:pl-4">
        <h2 className = "text-2xl font-semibold">Faruq</h2>
        <h3 className = "italic">faruq123@gmail.com</h3>
        </div>
      </div>
      <button className = "w-full py-3 bg-red-600 mt-4 text-white">Sign out</button>
    </div>
  ) 
};
export default ProfilePage;

With that done, let’s move to the PasswordReset component. This is where the user can input their email into a form and have a password reset email sent to them in case they lose their password. The code for this component is as follows:

import React, { useState } from "react";
import { Link } from "@reach/router";

const PasswordReset = () => {
  const [email, setEmail] = useState("");
  const [emailHasBeenSent, setEmailHasBeenSent] = useState(false);
  const [error, setError] = useState(null);
  const onChangeHandler = event => {
    const { name, value } = event.currentTarget;
    if (name === "userEmail") {
      setEmail(value);
    }
  };
  const sendResetEmail = event => {
    event.preventDefault();
  };
  return (
    <div className="mt-8">
      <h1 className="text-xl text-center font-bold mb-3">
        Reset your Password
      </h1>
      <div className="border border-blue-300 mx-auto w-11/12 md:w-2/4 rounded py-8 px-4 md:px-8">
        <form action="">
          {emailHasBeenSent && (
            <div className="py-3 bg-green-400 w-full text-white text-center mb-3">
              An email has been sent to you!
            </div>
          )}
          {error !== null && (
            <div className="py-3 bg-red-600 w-full text-white text-center mb-3">
              {error}
            </div>
          )}
          <label htmlFor="userEmail" className="w-full block">
            Email:
          </label>
          <input
            type="email"
            name="userEmail"
            id="userEmail"
            value={email}
            placeholder="Input your email"
            onChange={onChangeHandler}
            className="mb-3 w-full px-1 py-2"
          />
          <button
            className="w-full bg-blue-400 text-white py-3"
          >
            Send me a reset link
          </button>
        </form>
        <Link
         to ="/"
          className="my-2 text-blue-700 hover:text-blue-800 text-center block"
        >
          &larr; back to sign in page
        </Link>
      </div>
    </div>
  );
};
export default PasswordReset;

Passing down the current user with the Context API

Like I pointed out earlier, we will pass down the current user to all the components that need it using React’s Context API.

The Context API allows us to pass data down a tree of components without pass that data as props of intermediate components. Therefore, regardless of how deeply nested in a component tree a component may be, it will have easy access to whatever data is stored in the context. You can learn more about Context here.

To pass down the data from Context, we will use a provider component that will house the Application component in our App.js file.

You can create a providers folder in your src folder. Inside the providers folder, create a new file called UserProvider.jsx. Import createContext alongside React. We also need to import auth from our firebase.js file. With that done, let’s create and export our context, which we will call UserContext. Its initial value will be an object with a property called user, whose value is null.

export const UserContext = createContext({ user: null });

Now let’s create the actual UserProvider component. We need to add a piece of state in the UserProvider component called user. This piece of state will later be passed down to the other components as the value of the UserContext context.

We also need to perform some operations as soon as the UserProvider component mounts. Firebase gives us an observer called onAuthStateChanged, which we can set on the auth object to listen for changes in the state of the current user (when the user logs in and logs out).

We can also get the current user and some information about that user, such as uid, email, display name, and so on, with onAuthStateChanged. What we want to do is get the current user and set it as the value of the user state. In this case, our current user is userAuth.

componentDidMount = () => {
    auth.onAuthStateChanged(userAuth => {
      this.setState({ user: userAuth});
    });
};

Our UserProvider component can now render the provider component that comes with our UserContext and, in turn, house the child components passed to it. Our UserProvider component should look like this:

import React, { Component, createContext } from "react";
import { auth } from "../firebase";

export const UserContext = createContext({ user: null });
class UserProvider extends Component {
  state = {
    user: null
  };

  componentDidMount = () => {
    auth.onAuthStateChanged(userAuth => {
      this.setState({ user: userAuth});
    });
  };
  render() {
    return (
      <UserContext.Provider value={this.state.user}>
        {this.props.children}
      </UserContext.Provider>
    );
  }
}
export default UserProvider;

Once we have our UserProvider ready, we will use it to wrap our Application component in our App.js file.

import React from "react";
import Application from "./Components/Application";
import UserProvider from "./providers/UserProvider";
function App() {
  return (
    <UserProvider>
      <Application />
    </UserProvider>
  );
}
export default App;

Implementing Google sign-in

Implementing Google sign-in with Firebase is simple. Let’s go to our firebase.js file and create a variable called provider, whose value will be an instance of the Google provider object.

const provider = new firebase.auth.GoogleAuthProvider();

Firebase Auth provides different methods of signing in, such as signInWithRedirect, which redirects the user to a new page, and signInWithPopup, which makes use of a pop-up. After choosing our sign-in method, we can now write and export our simple Google sign-in function:

export const signInWithGoogle = () => {
  auth.signInWithPopup(provider);
};

We can now use this SignInWithGoogle function in the onClick handler functions for the Sign In with Google button in our SignIn and SignUp components.

Now, when you sign in with your Google account, you should be signed in to the application and taken to your profile page where you should see your display name, email, and display picture. However, because we are still using the placeholder values in the ProfilePage, this is not so. Also, the Sign out button doesn’t work so we need to modify our ProfilePage, like so:

import React, { useContext } from "react";
import { UserContext } from "../providers/UserProvider";
import {auth} from "../firebase";
const ProfilePage = () => {
  const user = useContext(UserContext);
  const {photoURL, displayName, email} = user;
  return (
    <div className = "mx-auto w-11/12 md:w-2/4 py-8 px-4 md:px-8">
      <div className="flex border flex-col items-center md:flex-row md:items-start border-blue-400 px-3 py-4">
        <div
          style={{
            background: `url(${photoURL || 'https://res.cloudinary.com/dqcsk8rsc/image/upload/v1577268053/avatar-1-bitmoji_upgwhc.png'})  no-repeat center center`,
            backgroundSize: "cover",
            height: "200px",
            width: "200px"
          }}
          className="border border-blue-300"
        ></div>
        <div className = "md:pl-4">
        <h2 className = "text-2xl font-semibold">{displayName}</h2>
        <h3 className = "italic">{email}</h3>
        </div>
      </div>
      <button className = "w-full py-3 bg-red-600 mt-4 text-white" onClick = {() => {auth.signOut()}}>Sign out</button>
    </div>
  ) 
};
export default ProfilePage;

As you can see, we used the useContext Hook to get the current value of UserContext and grabbed the necessary data from it. We also added an onClick handler to our sign-out button. This function uses the signOut method provided by the auth object.

Now you should be able to see your correct details on your profile page.

Implementing email/password sign-in

When we implement email/password authentication, we cannot add additional data such as display name on user creation. As a solution, we will save and retrieve each user’s data on Cloud Firestore. This article will not discuss Cloud Firestore in detail but will cover the parts of Cloud Firestore we need to understand. You can visit the docs to learn more about Firestore.

So what is Cloud Firestore, and how does it store data? Well, according to the docs:

Cloud Firestore is a NoSQL, document-oriented database. Unlike a SQL database, there are no tables or rows. Instead, you store data in documents, which are organized into collections.

Each document contains a set of key-value pairs. Cloud Firestore is optimized for storing large collections of small documents.

So, for our application, we will create a collection called users, which will contain documents for each user. These documents will contain information about the user, such as display name, email, and photo URL. We will also create an async function called generateUserDocument, which will then return the user data with the help of another function called getUserDocument.

Now, let’s write our generateUserDocument function in our firebase.js file. First of all, we need to get a reference to the user’s document in the users collection. We can do this with the doc method that Firestore provides. Once that’s done, we need to get the current content of the document, and we can do this with the get method of the user reference.

const userRef = firestore.doc(`users/${user.uid}`);
const snapshot = await userRef.get();

We now want to check if there is data at the specified reference. If there is no data, we want to write some data to that document. After that, we will return the user’s data using the getUserDocument function. If there is data, we will return the user’s data right away.

export const generateUserDocument = async (user, additionalData) => {
  if (!user) return;
  const userRef = firestore.doc(`users/${user.uid}`);
  const snapshot = await userRef.get();
  if (!snapshot.exists) {
    const { email, displayName, photoURL } = user;
    try {
      await userRef.set({
        displayName,
        email,
        photoURL,
        ...additionalData
      });
    } catch (error) {
      console.error("Error creating user document", error);
    }
  }
  return getUserDocument(user.uid);
};
const getUserDocument = async uid => {
  if (!uid) return null;
  try {
    const userDocument = await firestore.doc(`users/${uid}`).get();
    return {
      uid,
      ...userDocument.data()
    };
  } catch (error) {
    console.error("Error fetching user", error);
  }
};

Now that we have a function to host our users’ data on Firestore, let’s use that function in the createUserWithEmailAndPasswordHandler in our SignUp component. We can create a new user with email and password using the createUserWithEmailAndPassword method provided by Firebase, and then generate a user document for the new user with our new function, like so:

const createUserWithEmailAndPasswordHandler = async (event, email, password) => {
    event.preventDefault();
    try{
      const {user} = await auth.createUserWithEmailAndPassword(email, password);
      generateUserDocument(user, {displayName});
    }
    catch(error){
      setError('Error Signing up with email and password');
    }

    setEmail("");
    setPassword("");
    setDisplayName("");
  };

Next, let us modify the event handler for signing in using email and password in the SignIn component. We can sign in an already registered user using the signInWithEmailAndPassword method of the Firebase Auth object.

const signInWithEmailAndPasswordHandler = (event, email, password) => {
    event.preventDefault();
    auth.signInWithEmailAndPassword(email, password).catch(error => {
      setError("Error signing in with password and email!");
      console.error("Error signing in with password and email", error);
    });
  };

The final thing to do in order to fully implement the email/password sign-in is to modify the componentDidMount method of our UserProvider component. Since we are no longer retrieving data directly from the user object provided by the onAuthStateChanged method, we need to modify our code like so:

componentDidMount = async () => {
    auth.onAuthStateChanged(async userAuth => {
      const user = await generateUserDocument(userAuth);
      this.setState({ user });
    });
  };

Now, new users should be able to create an account in our application using their email and password.

Implementing the password reset feature

It is a good idea to provide a password reset feature so users can reset their passwords in the event they lose them.

Firebase makes this very easy to do with a handy function called sendPasswordResetEmail, which takes in an email as an argument and sends a reset link to the email if the user has an account with our application. If the user does not have an account with our app, this function throws an error. Let’s edit the sendResetEmail function in our PasswordReset component as follows:

const sendResetEmail = event => {
    event.preventDefault();
    auth
      .sendPasswordResetEmail(email)
      .then(() => {
        setEmailHasBeenSent(true);
        setTimeout(() => {setEmailHasBeenSent(false)}, 3000);
      })
      .catch(() => {
        setError("Error resetting password");
      });
  };

Now, registered users should be able to reset their passwords using the PasswordReset component.

Conclusion

Finally, we have come to the end of this article!

You should now be able to authenticate users in your React apps using Firebase. Firebase also supports other authentication providers such as GitHub, Twitter, and so on, and you can learn more about authentication in Firebase here.

You can also find the app we built live here.

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    Full visibility into production React apps

    Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

    LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

    The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

    Modernize how you debug your React apps — .

    Yusuff Faruq Frontend web developer and anime lover from Nigeria.

    30 Replies to “Handling user authentication with Firebase in your React apps”

    1. After creating an account you call generateUserDocument(user, {displayName}}). Should the result of this call be used to update the context? Also, onAuthStateChanged is triggered as soon as you register which calls generateUserDocument(userAuth). How do we now that generateUserDocument(user, {displayName}}) is called after generateUserDocument(userAuth)?

    2. Calling generateUserDocument(user, {displayName}) during account creation writes a new user document to firestore. The data contained in this document (the display name included) are now used to update the user context in onAuthStateChanged.

    3. Thanks for your response. So is the write operation guaranteed to complete before onAuthStateChanged is triggered?

      What if the onAuthChanged completes before the write operation? Wouldn’t we have an outdated user in the context?

    4. So far, this seems to work without problems for me.
      If you do, however, find any problems such as onAuthChanged completing before the write operation, I would be glad to look into it.

    5. i am sorry,
      but you hidden the firebase info in the article

      const firebaseConfig = {
      apiKey: ‘AIzaXXXXXXXXXXXXXXXXXXXXXXX’,
      authDomain: ‘test-XXXX.firebaseapp.com’,
      databaseURL: ‘https://test-XXXXXX.firebaseio.com’,
      projectId: ‘test-XXXX’,
      storageBucket: ‘test-XXXX.appspot.com’,
      messagingSenderId: ‘XXXXXXX’,
      appId: “XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”
      };

      but with developer tools i can see the data in plain text

      const firebaseConfig = {
      apiKey: “AIzaSyA9WZB5N6ekNxyN3yGaUwjuBilvXItUv38”,
      authDomain: “fir-auth-article.firebaseapp.com”,
      databaseURL: “https://fir-auth-article.firebaseio.com”,
      projectId: “fir-auth-article”,
      storageBucket: “fir-auth-article.appspot.com”,
      messagingSenderId: “774252759419”,
      appId: “1:774252759419:web:e014ddfa3553a4832a15de”,
      measurementId: “G-77Z5WJ0SET”
      };

      are u sure this tutorial is safe to be taken as an example?

    6. Hi, I just directly downloaded your github repo and changed the API details for firebase.
      I keep getting this error. Any help?
      Unhandled Rejection (FirebaseError): Failed to get document because the client is offline.

    7. Hi, thanks for this super helpful article! I’ve noticed that on page refresh, when the user is signed in, it flashes the authentication page before switching to the profile page. Do you have any tips on a best approach to solve this? Thanks!

    8. Hi, I’ve been trying to render a spinner until the object is loaded but I can’t find a way to do it. Could you give a little more detail on how to implement this? Thank you!

    9. It have the exact same issue as described by Nuur, during the account creation after the createUserWithEmailAndPassword I’m updating the user profile using updateProfile (https://firebase.google.com/docs/reference/js/firebase.User#updateprofile not storing in firestore) but in the meantime the onAuthStateChanged is triggered and so the register page component is unmounted (because i’m routing the /register to / if a user is connected). 2 main issue here : 1) the user state is outdated because onAuthStateChanged is triggered before the complete account creation (missing the displayName) and the 2) can’t catch the error of the 2nd async call (User.updateProfile) because the component is unmounted.

    10. I’m seeing the same as Nuur and Di2pra. Didn’t notice it until I started using the sign in with email and password. Has anyone found a decent workaround?

    11. Amazing tutorial! This helped me set up my own project. Maybe link the repo on top of the article for new readers?
      Huge thanks and have a good day!

    12. I am trying to update the code from React.createClass To extends React.Component – I hit an issue when I convert from ‘const Home = () => {‘ to ‘class Home extends useContext {‘ because of the following lines:

      const user = useContext(UserContext);
      const {photoURL, displayName, email} = user;

      How should I re-format these lines?

      Thank you!

    13. I’m just learning React and this has been an amazing guide. Really got me in the right headspace for using Firebase with React. You even got me learning about Hooks which I hadn’t gotten around to yet, so thanks for that too!

    14. @Faruq – Thanks for the write up. Pretty good information here.

      Just an FYI, in the write up you forget to mention after creating the UserContext/Provider – that you need to go back into your Application.js file and change the line:

      const user = null

      TO

      const user = useContext(UserContext);

      This is an important part of the application logic change, otherwise it will never detect the sign in and will never re-route to Profile page.

    15. Hey @Faruq

      Having issues on the destructuring in the profile page

      here is the error

      TypeError: Cannot destructure property ‘photoURL’ of ‘user’ as it is null.
      Welcome

      const user = useContext(UserContext);
      const {photoURL, displayName, email} = user;
      console.log(user);

      I tried removing the photoURL then got this

      TypeError: Cannot destructure property ‘displayName’ of ‘user’ as it is null.
      wq

      same with third property

      TypeError: Cannot destructure property ’email’ of ‘user’ as it is null.

      If I simply console log user I am getting the userobject properly But cant seem to access any of the properties

      this is my setup

      “dependencies”: {
      “@material-ui/core”: “^4.10.2”,
      “@material-ui/icons”: “^4.9.1”,
      “@material-ui/lab”: “^4.0.0-alpha.56”,
      “@testing-library/jest-dom”: “^4.2.4”,
      “@testing-library/react”: “^9.5.0”,
      “@testing-library/user-event”: “^7.2.1”,
      “firebase”: “^7.15.1”,
      “react”: “^16.13.1”,
      “react-dom”: “^16.13.1”,
      “react-firebase”: “^2.2.8”,
      “react-router-dom”: “^5.2.0”,
      “react-scripts”: “3.4.1”
      },

      Can you tell me whats going wrong

      Thanks for the help

    16. I’m having the same issues as @Mark when it comes to retrieving the props on the profile page, they show up as null. Could you provide any help? The article was great and has been a huge help so far

    17. To get around the issue of the issue of the authOnStateChanged event listener firing before the user document has been written to firestore, I created a separate method named loadUser which through the use of setInterval polls firestore every second and checks to see if the document is written. I set it up to only make 3 attempts after which it stops. When you’ve reached the number of retries or you’ve gotten the full user document you can called clearInterval to stop the polling. This is really a poorly written hack and I’m still looking for a more elegant solution.

      class UserProvider extends React.Component {
      constructor(props) {
      super(props);
      this.state = { currentUser: null, isLoggedIn: false };
      this.loadUser = this.loadUser.bind(this);
      }

      componentDidMount() {
      this.retries = 3;
      this.authListener = auth.onAuthStateChanged(userAuth => {
      if (userAuth) {
      this.intervalId = setInterval(this.loadUser, 1000, userAuth.uid);
      console.log(this.intervalId);
      } else {
      this.setState({ currentUser: null, isLoggedIn: false });
      }
      });
      }

      componentWillUnmount() {
      this.authListener();
      }

      async loadUser(userId) {
      console.log(`Load user called. Number of retries: ${this.retries}`);
      if (this.retries === 1) {
      clearInterval(this.intervalId);
      }
      try {
      const currentUser = await getUserDocument(userId);
      if (currentUser.email) {
      this.setState({ currentUser, isLoggedIn: true });
      clearInterval(this.intervalId);
      }
      } catch (error) {
      console.log(`Error logging the user in: ${error}`);
      clearInterval(this.intervalId);
      } finally {
      this.retries -= 1;
      }
      }

    18. Sir I am facing issue in this tutorial, need a help to solve the issue. Please help me out with the problem, I am stuck with sendResetEmail function in passwordreset component

    19. Thanks for the post, it seems most missed the preface of being somewhat experienced with React. Brilliant explanation for an introduction to authentication within React, Thanks Again!

    20. Hey, Yussuf,
      Your code works like a charm for my project, I adapted it to my code for resseting password in Firebase.
      Thx a lot!

    Leave a Reply