How to authenticate a React App with Firebase

Today, we will create a simple React App with the functionality of Sign In, Sign Up, and Logout. We will also integrate our app with Firebase Cloudstore in order to save the user’s data.

Let’s begin by creating a new React App by using the npx create-react-app myApp command and do the necessary clean-up after the app has been created.

This includes removing any:

  • Test files
  • Logos
  • Commented-out code

Let’s install Firebase in our React App by typing in the command line:

npm i firebase

You also need to navigate to:

After your project is created:

  • Go to the Web icon
  • Give a name to your app
  • Click Register app

Then, you will get a block of code which you need to copy, and you will create a firebase.js file in the src folder of your React App.

Paste that code in the following manner:

Alt Text

You need to click Authentication and then Set sign up method in the Firebase console of your project. Here, we will enable the first option, Email/Password, and click Save.

Note: The users tab will show us the list of all the users who have signed up in our application.

Alt Text

Let’s go back to our React App and start adding the necessary code to make our app functional. First, we will create state variables for all the states that our app will have. We will define the state variables in App.js using the useState hook:

const [user, setUser] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [emailError, setEmailError] = useState("");
  const [passwordError, setPasswordError] = useState("");
  const [hasAccount, setHasAccount] = useState(false);

We will create a components folder in src directory and define a login.js component. Here, we will have the login form (email and password) and also the Sign In and Sign Up buttons:

const Login = () => {
  
    return (
        <div className="login">

        
        <div className="loginContainer">
            
         <label>Username</label>
         <input type="text" autoFocus required value={email} onChange={(e)=> setEmail(e.target.value)}/>

           <p className="errorMsg">{emailError}</p>

           <label>Password</label>
           <input type="password" autoFocus required value={password} onChange={(e)=> setPassword(e.target.value)}/>
           <p className="errorMsg">{passwordError}</p>

           <div className="btnContainer">
               {hasAccount ? (
                    <>
                    <button onClick={handleLogin}>Sign In</button>
                    <p>Don't have an account ? <span onClick={() => setHasAccount(!hasAccount)}>Sign Up</span></p>
                    </>
               ) : (
<>
                    <button onClick={handleSignUp}>Sign Up</button>
                    <p>Already have an account .. <span onClick={() => setHasAccount(!hasAccount)}>Sign In</span></p>
                    </>
               )}

           </div>
        </div>
        </div>
    )
}

export default Login

Notice that we have defined the handleLogin and handleSignUp functions here. However, they are missing from our App.js file, so we will have to create them:

const handleLogin = () => {
    firebasedb
      .auth()
      .signInWithEmailAndPassword(email, password)
      .catch((err) => {
        switch (err.code) {
          case "auth/Invalid-email":
          case "auth/user-disabled":
          case "auth/user-not-found":
            setEmailError(err.message);
            break;
          case "auth/wrong-password":
            setPasswordError(err.message);
            break;
          default:
        }
      });
  };

The handleLogin function uses the Firebase auth() and signInWithEmailAndPassword(email, password) methods to validate the email and password the user has entered. This throws an error if the information entered is incorrect or not found.

Similarly, we will define the handleSignUp method:

const handleSignUp = () => {
    firebasedb
      .auth()
      .createUserWithEmailAndPassword(email, password)
      .catch((err) => {
        switch (err.code) {
          case "auth/email-already-in-use":
          case "auth/invalid-email":
            setEmailError(err.message);
            break;
          case "auth/weak-password":
            setPasswordError(err.message);
            break;
          default:
        }
      });
  };

We will define a handleLogout function here:

  const handleLogout = () => {
    firebasedb.auth().signOut();
  };

We will also define an authListener() method, which will be activated on every auth state change. This will let us know if a valid user exists on our application:

const authListener = () => {
    firebasedb.auth().onAuthStateChanged((user) => {
      if (user) {
        clearInputs();
        setUser(user);
      } else {
        setUser("");
      }
    });
  };

We will need a useEffect hook, which will call the authListener():

useEffect(() => {

    authListener();
  }, []);
  • Next, we need to pass the props in the Login component so that we can import them in the App.js file.
  • We will also define a Home.js component, which will be displayed when the user has logged in.
  • We will switch these depending on the user i-e.
  • We will show the home component if the user exists. Otherwise, we will display the login component:
return (
    <div className="App">
      {user ? (
        <Home handleLogout={handleLogout} />
      ) : (
        <Login
          email={email}
          setEmail={setEmail}
          password={password}
          setPassword={setPassword}
          handleLogin={handleLogin}
          handleSignUp={handleSignUp}
          hasAccount={hasAccount}
          setHasAccount={setHasAccount}
          emailError={emailError}
          passwordError={passwordError}
        />
      )}
    </div>
  );

Don’t forget to import the (props) in the login component:

const Login = (props) => {
  
const {email, password, setEmail, setPassword, handleLogin, handleSignUp,hasAccount,setHasAccount, emailError, passwordError} = props;

    return (
        <div className="login">

        
        <div className="loginContainer">
            
           <label>Username</label>
           <input type="text" autoFocus required value={email} onChange={(e)=> setEmail(e.target.value)}/>

           <p className="errorMsg">{emailError}</p>

           <label>Password</label>
           <input type="password" autoFocus required value={password} onChange={(e)=> setPassword(e.target.value)}/>
           <p className="errorMsg">{passwordError}</p>

           <div className="btnContainer">
               {hasAccount ? (
                    <>
                    <button onClick={handleLogin}>Sign In</button>
                    <p>Don't have an account ? <span onClick={() => setHasAccount(!hasAccount)}>Sign Up</span></p>
                    </>
               ) : (
<>
                    <button onClick={handleSignUp}>Sign Up</button>
                    <p>Already have an account .. <span onClick={() => setHasAccount(!hasAccount)}>Sign In</span></p>
                    </>
               )}

           </div>
        </div>
        </div>
    )
}

export default Login

Afterward, you will sign up to the application and navigate to your Firebase console. You will see that the list of users will show the user you have just created.

This brings us to the end of our application. Happy coding!

Attributions:
  1. undefined by undefined