React Firebase Auth with Redux deployed on Vercel
Tags: react, redux, firebase, webdev
Published: 2024-02-07 12:33:22

## Motivation
Temporarily, I'm a full-time front-end developer for a project. It utilizes Redux, but when I started, I lacked both React and Redux experience. While I've been on this project for three months now, I've primarily relied on existing code to implement Redux functionalities without grasping the core concepts. Understanding the intricacies of the Redux store and its inner workings remained elusive. Thankfully, with guidance from senior colleagues and Stack Overflow, I've managed to navigate runtime issues.

However, I'm determined to delve deeper and solidify my understanding of Redux from the ground up. This way, I can troubleshoot problems with confidence and rely less on external resources for minor challenges.

To efficiently learn two technologies simultaneously, I decided to implement Firebase Authentication while storing user metadata and auth tokens using Redux.

---

## Setup (Firebase)

 **Create a Firebase project**:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hclt9jx9e76bpq23v7le.png)

Go to the Firebase console (https://console.firebase.google.com/) and create a new project.
Give your project a name and enable billing if necessary.

 **Register your app**:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d91ub03fh0k0d1b680t6.png)

Select the platform you're developing for (web, Android, iOS, etc.) and follow the specific instructions for registering your app.
This will give you a configuration file or snippet that you need to add to your project.
** Install the Firebase SDK:**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxx01yjd4ixsvnkef61h.png)
Use the package manager for your platform (npm for JavaScript, CocoaPods for iOS, etc.) to install the required Firebase SDK libraries.
 **Initialize Firebase in your app:**

Use the configuration file or snippet you obtained earlier to initialize Firebase in your app.
This will enable you to use Firebase services in your code.

 **Create Register Component**

_Notice, I the code I also have sent another call to `sendEmailVerification` so user cannot register with a bogus email_
 
---


```
const Register = () => {
  const navigate = useNavigate();
  const [loading, setLoading] = useState(false);
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const register = async () => {
    setLoading(true);
      await createUserWithEmailAndPassword(auth, email, password)
        .then(async (userCredential: any) => {
          await sendEmailVerification(userCredential.user);
          navigate(RouteEnums.VERIFICATION_NOTICE, { state : userCredential.user.email });
        })
        .catch((error: any) => {
          toast.error(error.message);
        }).finally(()=> {
          setLoading(false)
        });
  };

  return (
    <>
      {loading ? <Spinner /> : (
           <div className="flex min-h-full flex-1 flex-col justify-center w-[400px] px-6 py-12 lg:px-8">
           <div className="sm:mx-auto sm:w-full sm:max-w-sm">
             <img
               className="mx-auto h-10 w-auto"
               src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
               alt="Your Company"
             />
             <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
               Register a new account
             </h2>
           </div>
   
           <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
             <div>
               <label
                 htmlFor="email"
                 className="block text-sm font-medium leading-6 text-gray-900"
               >
                 Email address
               </label>
               <div className="mt-2">
                 <input
                   onChange={(e) => setEmail(e.target.value)}
                   id="email"
                   name="email"
                   type="email"
                   autoComplete="email"
                   required
                   className="block w-full rounded-md border-0 p-1.5 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                 />
               </div>
             </div>
   
             <div className="my-5">
               <div className="flex items-center justify-between">
                 <label
                   htmlFor="password"
                   className="block text-sm font-medium leading-6 text-gray-900"
                 >
                   Password
                 </label>
                 <div className="text-sm">
                   <a className="font-semibold text-indigo-600 hover:text-indigo-500">
                     Forgot password?
                   </a>
                 </div>
               </div>
               <div className="mt-2">
                 <input
                   onChange={(e) => setPassword(e.target.value)}
                   id="password"
                   name="password"
                   type="password"
                   autoComplete="current-password"
                   required
                   className="block w-full rounded-md border-0 p-1.5  shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                 />
               </div>
             </div>
   
             <div>
               <button
                 onClick={register}
                 type="submit"
                 className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
               >
                 Register
               </button>
             </div>
           </div>
         </div>
      )}
   
    </>
  );
};
```

**Verify the Register Function**

_If your configuration was successful your user would appear in the `Authentication > Users `section on the firebase console_

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3u542r10gjqyrrwowp8c.png)

**Create Login component**

_Notice I have only let user into my application if the user's email is verified_

```
const Login = () => {
  const navigate = useNavigate();
  const dispatch = useDispatch();
  const [loading, setLoading] = useState(false);

useEffect(() => {
  console.log(loading)
},[loading])

  const checkEmailVerified = (userCredential: any) => {
    if (!userCredential.user.emailVerified) {
      console.error("User email is not verified.");
      toast.error("Please verify your email address to continue.");
    } else {
      dispatch(saveUser(userCredential.user));
      dispatch(saveToken(userCredential.user.accessToken));
      navigate(RouteEnums.HOME);
    }
  };
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const login = () => {
    setLoading(true);
      signInWithEmailAndPassword(auth, email, password)
        .then((userCredential: any) => {
          checkEmailVerified(userCredential);
        })
        .catch((error: any) => {
          toast.error(error.message);
        }).finally(()=> {
          setLoading(false)
        });
  };

  return (
    <>
      {loading ? <Spinner /> : (
              <div className="flex min-h-full flex-1 flex-col justify-center w-[400px] py-12 lg:px-8">
              <div className="sm:mx-auto sm:w-full sm:max-w-sm">
                <img
                  className="mx-auto h-10 w-auto"
                  src="https://tailwindui.com/img/logos/mark.svg?color=indigo&shade=600"
                  alt="Your Company"
                />
                <h2 className="mt-10 text-center text-2xl font-bold leading-9 tracking-tight text-gray-900">
                  Login
                </h2>
              </div>
      
              <div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
                <div>
                  <label
                    htmlFor="email"
                    className="block text-sm font-medium leading-6 text-gray-900"
                  >
                    Email address
                  </label>
                  <div className="mt-2">
                    <input
                      onChange={(e) => setEmail(e.target.value)}
                      id="email"
                      name="email"
                      type="email"
                      autoComplete="email"
                      required
                      className="block w-full rounded-md border-0 p-1.5 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                    />
                  </div>
                </div>
      
                <div className="my-5">
                  <div className="flex items-center justify-between">
                    <label
                      htmlFor="password"
                      className="block text-sm font-medium leading-6 text-gray-900"
                    >
                      Password
                    </label>
                    <div className="text-sm">
                      <a className="font-semibold text-indigo-600 hover:text-indigo-500">
                        Forgot password?
                      </a>
                    </div>
                  </div>
                  <div className="mt-2">
                    <input
                      onChange={(e) => setPassword(e.target.value)}
                      id="password"
                      name="password"
                      type="password"
                      autoComplete="current-password"
                      required
                      className="block w-full rounded-md border-0 p-1.5  shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                    />
                  </div>
                </div>
      
                <div>
                  <button
                    onClick={() => login()}
                    type="submit"
                    className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
                  >
                    Login
                  </button>
                </div>
              </div>
            </div>
      )}

    </>
  );
};
```

🎉 Congratulations 🎉 you have implemented Auth using Firebase in your React app

## **Now Lets save the response from firebase in the Redux Store**

**Install the required packages:**

`Bash`

```
npm install @reduxjs/toolkit react-redux @types/react-redux @types/react-router-dom (if using routing)
```

Store Setup:

**Create a store.ts file to define your Redux store:**

```
`import { configureStore } from "@reduxjs/toolkit";
import authReducer from "../reducer/user/userAuthSlice"


export const store = configureStore({
    reducer: {
        auth: authReducer,
        }
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch`

```

**Configure Reducers**



```
import { createSlice } from "@reduxjs/toolkit"

const initialState: any = {
    user: null,
    authToken : null
}
const userAuthSlice = createSlice({
    name : "auth",
    initialState,
    reducers: {
        saveUser(state :any, action:any){
            state.user = action.payload
        },
        saveToken(state :any, action:any){
            state.authToken = action.payload
        }
    }
},
)


export const { saveUser , saveToken } : any = userAuthSlice.actions


export default userAuthSlice.reducer
```
**Store the user Data and token in stores on successful login**

```
      dispatch(saveUser(userCredential.user));
      dispatch(saveToken(userCredential.user.accessToken));
```

**To protect the application create a guard to only allow authenticated user to login**


```
const Router = () => {
  const GUARD_ROUTE = (props: any) => {
    const { children } = props;
    const authToken: any = useSelector((state: any) => state.auth.authToken);
    if (authToken) {
      return <React.Fragment>{children}</React.Fragment>;
    } 
    if (!authToken) {
      toast.dismiss()
      toast.error("Please Login First");
    }
    return <Navigate to={RouteEnums.LANDING} />;
  };
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Landing />} />
        <Route path="/login" element={<Login />} />
        <Route path="/register" element={<Register />} />
        <Route path="/verification-sent" element={<VerificationNotice />} />
        <Route
          path="/home"
          element={
            <GUARD_ROUTE>
              <Home />
            </GUARD_ROUTE>
          }
        />
      </Routes>
    </BrowserRouter>
  );
};

export default Router;
```

🎉 Congratulations you have implemented Redux as well 🎉 

## Deployment 

I used Vercel as it is the easiest and quickest option.

**Steps**

**Push your app to a Git repository:**

If you don't have one, create a new repository on GitHub, GitLab, or Bitbucket.
Push your local React app files to the remote repository.

**Import your repository into Vercel:**

- Go to https://vercel.com/dashboard
- Click "New Project" and select "From Git repository".
- Choose your Git provider and authorize Vercel to access your repositories.
- Select your repository and click "Import".
- Vercel will automatically detect your React app and deploy it. 


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/npawdx10bndg0heo3zdm.png)



**You'll receive a deployment URL upon completion.**