Project 50 of 100 - Firebase Sign Up and Login with React Router
Tags: react, javascript, 100daysofcode
Published: 2021-02-23 06:15:36

*Hey! I'm on a mission to make 100 React.js projects ending March 31st. Please follow my dev.to profile or my [twitter](https://www.twitter.com/jwhubert91) for updates and feel free to reach out if you have questions. Thanks for your support!*

Link to the deployed project: [Link](https://100-react-projects-day-50-compras-auth.netlify.app/)
Link to the repo: [github](https://github.com/jwhubert91/100daysofreact/tree/master/day-50-react-router-firebase-auth)

Today I made an authentication flow using React, React Router, and Firebase. I adapted the structure [from an earlier project]() for something new with a new UI here, but the main principles are the same. This will just be a short post highlighting the main functionalities here and how they're implemented.

We just have four components- the `App` component, `SignUp`, `Login` and `GlobalNavbar`. I have other components in the repo but they're not being used so feel free to ignore them. All of the magic happens in the App component where we import `react-router-dom` for our routing functionality. 

```
# App.js

import React,{useState} from 'react';
import {auth} from './firebase';
import {
  BrowserRouter as Router,
  Route,
  Switch,
  Link
} from 'react-router-dom';

import SignUp from './pages/SignUp';
import SignIn from './pages/SignIn';
import GlobalNavbar from './components/GlobalNavbar';
```

As you can see we also import a custom {auth} object that we created in a local `firebase` file. That's just a js file we store in the src folder that imports the relevant firebase node modules and initializes them, and their connection to Firebase:

```
# firebase.js

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

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID,
  measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID
};

// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();

export const auth = firebase.auth();
export default firebase;
```

As you can see I am storing that sensitive Firebase API information in environment variables. The node package `nodenv` allows us to create environment variables that can easily be left out of git commits by creating a .env file in the root of the project folder and putting our React App variables there with the following format:

```
REACT_APP_API_KEY=123123123123
```

You can then access those variables (after a server restart) by calling process.env.REACT_APP_API_KEY in your src folder files. Make sure those variables start with REACT_APP_ or CRA won't pick them up.

Anyway, the firebase.js file above initializes the connection to Firebase and imports the relevant methods for Firebase auth, analytics, and firestore. We export firebase.auth() just for convenience and brevity.

I trust you know how to make a form in React using text inputs- so I won't go over those. You just need an email and password text inputs plus a button to make this work. I'll just go over the Firebase methods used here:

To sign up a user with an email simply use `firebase.auth().createUserWithEmailAndPassword(email,password)` where email and password are text strings. I do this in the following function (after some basic validation):

```
  const handleSignUp = () => {
    if (handleConfirmPassword()) {
      // password and confirm password match!
      auth.createUserWithEmailAndPassword(email,password)
        .then(result => {
          alert(email+' signed in successfully',clearFields());
        })
        .catch(function(error) {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        if (errorCode === 'auth/weak-password') {
          alert('The password is too weak.');
        } else {
          alert(errorMessage);
        }
        console.log(error);
      });
      clearFields()
    }
  }
```

This function will alert the user whether or not the submission was successful and tell the user why if there was an error.

In the SignIn page we have a similar setup. A simple form that takes an email and password. For that the functionality is very similar and we use the `firebase.auth().ignInWithEmailAndPassword(email, password)` method like so:

```
  const logUserIn = () => {
    auth.signInWithEmailAndPassword(email, password)
      .then(result => {
        alert(email+' signed in successfully',clearFields());
      })
      .catch(function(error) {
      // Handle Errors here.
      const errorCode = error.code;
      const errorMessage = error.message;
      if (errorCode === 'auth/weak-password') {
        alert('The password is too weak.');
      } else {
        alert(errorMessage);
      }
      console.log(error);
    })
  }
```

These two methods are the heart are sign in and sign up with Firebase, which takes a lot of pain out of your authentication flow.

After we've imported the pages into App.js we put them into a React Router Switch like so (with the `GlobalNavbar` component on top of everything so it is present regardless of the page we're on):

```
  return (
    <div className="App">
      <Router>
        <GlobalNavbar />
        <Switch>
          <Route path='/login'>
            <SignIn />
          </Route>
          <Route>
            <SignUp path='/' />
          </Route>
        </Switch>
      </Router>
    </div>
  );
```

I haven't done anything with it yet in this application, but the Firebase method to check if there is a logged in user or not is the following:

```
const [userExists,setUserExists] = useState(false);

auth.onAuthStateChanged((user) => {
    if (user) {
      setUserExists(true);
      console.log('Signed in as '+user.email);
    } else {
      setUserExists(false);
    }
  });
```

If you get creative you can imagine using that piece of `userExists` state to automatically route a user to a main dashboard or other authenticated page if they're logged in.

Lastly, I just want to tell you about what you need to do to make an app like this work on Netlify. This app really relies on React Router working, but React Router and Netlify don't necessarily play well together. In fact, if you just upload a project with React Router to Netlify it won't work, and when you try to follow a redirect Netlify will show you a "Page does not exist" error.

So, to deal with this, before we build the project we've got to add a file called _redirects to the public folder. This tells Netlify that any redirects will come back to the index.html page that is the root of your project.

I followed [this](https://community.netlify.com/t/netlify-page-not-found-when-sharing-react-router-dom-based-links/11744/6) and [this](https://dev.to/rajeshroyal/page-not-found-error-on-netlify-reactjs-react-router-solved-43oa) to get it going. Ultimately- it's just a matter of putting the following single line of code into that _redirects file:

```
/* /index.html 200
```

That's it! Sorry it's not as detailed today- but check the code in the repo and I'm sure you can follow along. As usual if you get stuck don't be afraid to ping me in the comments :)