DEV Community

jagarkin
jagarkin

Posted on

Firebase auth with React

hello today I'll be talking on how I build a simple yet minimalistic to do app with react and firebase your see the app here devdo I will be highlighting the most important stuff feel free to check the code for more.

Alt Text

let's start by installing our dependencies

npm install react-icons --save
npm install --save styled-components
npm install react-tabs
npm i firebase
Enter fullscreen mode Exit fullscreen mode

this is the tree for the code
Alt Text

I started by creating a router with three component:

  • Log in
  • Sign UP
  • Reset Password
  • Dashboard
  • Update Profiles

then I initialized the firebase SDK in in the Firebase.js folder I left the config in the code as it was only for testing

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

const app = firebase.initializeApp({
  apiKey: "Xxxx Xxxx Xxxx Xxxx Xxxx",
  authDomain: "Xxxx.firebaseapp.com",
  projectId: "Xxxx",
  storageBucket: "Xxxx Xxxx.appspot.com",
  messagingSenderId: "Xxxx Xxxx ",
  appId: "Xxxx Xxxx Xxxx Xxxx Xxxx"
});

export const auth = app.auth();
export const useFireStore = firebase.firestore();

export default app;
Enter fullscreen mode Exit fullscreen mode

don't forgert to activate our auth methode from the firebase console in our case it's only email and password
Alt Text

create an AuthProvider so that we have firebase auth on top

import React, { useContext, useEffect, useState } from "react";
import { auth } from "./Firebase";

const AuthContext = React.createContext();

export function useAuth() {
  return useContext(AuthContext);
}

export default function AuthProvider({ children }) {
  const [loading, setLoading] = useState(true);
  const [currentUser, setCurrentUser] = useState();

  const signup = (email, password) => {
    return auth.createUserWithEmailAndPassword(email, password);
  };

  const login = (email, password) => {
    return auth.signInWithEmailAndPassword(email, password);
  };

  const logout = () => {
    return auth.signOut();
  };

  const resetPassword = email => {
    return auth.sendPasswordResetEmail(email);
  };

  const updateEmail = email => {
    return auth.updateEmail(email);
  };

  const updatePassword = password => {
    return currentUser.updatePassword(password);
  };

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(user => {
      setCurrentUser(user);
      setLoading(false);
    });
    return unsubscribe;
  }, []);

  const value = {
    currentUser,
    signup,
    login,
    logout,
    resetPassword,
    updateEmail,
    updatePassword
  };

  return (
    <AuthContext.Provider value={value}>
      {!loading && children}
    </AuthContext.Provider>
  );
}

Enter fullscreen mode Exit fullscreen mode

then we need a protected route to prevent non logged users to access certain component

import React from "react";
import { Redirect, Route } from "react-router-dom";
import { useAuth } from "../utils/AuthContext";

const PrivateRoute = ({ component: Component, ...rest }) => {
  const { currentUser } = useAuth();

  return (
    <>
      <Route
        {...rest}
        render={props => {
          return currentUser ? (
            <Component {...props} />
          ) : (
            <Redirect to='/login' />
          );
        }}></Route>
    </>
  );
};

export default PrivateRoute;
Enter fullscreen mode Exit fullscreen mode

then in our App.js we create our routing as you'll notice that we wrapped everything in an app provider

import Login from "./components/services/Login";
import Signup from "./components/services/Signup";
import Dashboard from "./components/pages/Dashboard";
import PrivateRoute from "./components/utils/PrivateRoute";
import AuthProvider from "./components/utils/AuthContext";
import UpdateProfile from "./components/services/UpdateProfile";
import ForgotPassword from "./components/services/ForgotPassword";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";

function App() {
  return (
    <>
      <AuthProvider>
        <Router>
          <Switch>
            <PrivateRoute exact path='/' component={Dashboard} />
            <PrivateRoute path='/update-profile' component={UpdateProfile} />
            <Route path='/signup' component={Signup} />
            <Route path='/login' component={Login} />
            <Route path='/forgot-password' component={ForgotPassword} />
          </Switch>
        </Router>
      </AuthProvider>
    </>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

now after creating our auth service let's hover on something and it's how each user will have it's own data ? it's easy our collection will be the same uid of the currentUser and we need subcollection beceause we have different sections for the notes taken
in Input.js should be an async methode like this

const nametRef = useRef();
  const textRef = useRef();

  const { currentUser } = useAuth();
  const [error, setError] = useState("");

  const pushData = async e => {
    e.preventDefault();

    if (nametRef.current.value === "" && textRef.current.value === "") {
      setError("take note");
    } else {
      setError("");
      try {
        return await useFireStore
          .collection(`${currentUser.uid}`)
          .doc("socket")
          .collection(`${docs}`)
          .add({
            noteName: nametRef.current.value,
            contentNote: textRef.current.value,
            createdAt: firebase.firestore.FieldValue.serverTimestamp()
          });
      } catch {
        setError("please verify your connection");
      }
    }
  };
Enter fullscreen mode Exit fullscreen mode

it goes the same for fetching data from the firestore as we saved the name of our collection with the same current user id
note that our methode must be async so it will always listen to the change from the server side

const { currentUser } = useAuth();
  const [error, setError] = useState("");
  const [sockets, setSockets] = useState([]);

  const pullData = async () => {
    return await useFireStore
      .collection(`${currentUser.uid}`)
      .doc("socket")
      .collection(`${docus}`)
      .orderBy("createdAt", "desc")
      .onSnapshot(snapshot => {
        const data = snapshot.docs.map(doc => ({
          id: doc.id,
          ...doc.data()
        }));
        data.length === 0 ? setError("no notes yet") : setError("");
        setSockets(data);
      });
  };

  useEffect(() => {
    // we use pull effect to ovoid memory leak
    pullData();
  }, []);
Enter fullscreen mode Exit fullscreen mode

and to delete a document in the jsx we add this code

{sockets.map((socket, index) => {
          return (
            <Fetched key={index}>
              <Div>
                <Text>{socket.noteName}</Text>
                <Disc>{socket.contentNote}</Disc>
              </Div>

              <Delete
                onClick={() => {
                  useFireStore
                    .collection(`${currentUser.uid}`)
                    .doc("socket")
                    .collection(`${docus}`)
                    .doc(`${socket.id}`)
                    .delete();
                }}
              />
            </Fetched>
          );
        })}
        {error && <p>{error}</p>}
Enter fullscreen mode Exit fullscreen mode

and voila like that we have a fully functioning to do app with auth and each user have the abilty to see it's own data and delete it
PS: code it not fully explained as I said on top, I am highlighting the important thing i did to make it fully function

Top comments (1)

Collapse
 
iamaravindks profile image
Aravind K Subash

Hai , can you explain the logic behind the clean uping of onAuthStateChanged.I can't figure it out . Please help me :)