0

I am trying to do backend for my website and I have login, signup, reset password etc. all working. What I am trying to do now is when user log in or sign up, AuthContext to check for file that match his UID and if exist store it to variable or if not exist create it and store it to variable. Just cant get it work. Best i got so far is code at bottom but problem there is when I log out user I am getting all sort errors because user not exists anymore.

my context file look like this so far and everything is working:

import { createContext, useContext, useEffect, useState } from "react";
import { auth, db } from "../firebase";

export const AuthContext = createContext();

export const useAuth = () => {
  return useContext(AuthContext);
};

const AuthContextProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState(null);
  const [currentUserDoc, setCurrentUserDoc] = useState(null);

  const [loading, setLoading] = useState(true);

  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);
  };

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

    return unsubscribe;
  }, []);

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

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

export default AuthContextProvider;

I tryed to change useEffect hook to this but can't get it done right:

 useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(async (user) => {
      setCurrentUser(user);

      const userDoc = db.collection("users").doc(user.uid);
      
      await userDoc.get().then((doc) => {
        if (doc.exists) {
          setCurrentUserDoc(doc.data());
        } else {
          doc.set({
            email: user.email,
            first_name: "",
            last_name: "",
            country: "",
            organization: "",
          });
        }
      });

      setLoading(false);
    });

    return unsubscribe;
  }, []);

This is error when there is no user logged in:

this is problem when i am logged out

Code where I am trying to use it:

import styles from "./Header.module.scss";

import { useAuth } from "../../contexts/AuthContext";

const Header = () => {
  const { logout, currentUserDoc } = useAuth();

  return (
    <header className={styles.header}>
      <div></div>
      <div className={styles.header__user}>
        {currentUserDoc.email}
        <button onClick={logout}>Log out</button>
      </div>
    </header>
  );
};

export default Header;
Dharmaraj
  • 47,845
  • 8
  • 52
  • 84
Klak031
  • 97
  • 1
  • 1
  • 13
  • 1
    What is not working as intended? Also add an `await` before `doc.set()` and make it's parent function async – Dharmaraj Jul 29 '21 at 16:29
  • Is not working when I log out for example I am getting error can't read property UID of null and my application crash at that point.. this works only when user is logged in. what I am trying to achieve is check if user is logged in -> set user -> read user file -> set user file -> render page (in my cace setLoading(false)) – Klak031 Jul 29 '21 at 16:33
  • If the user logs out then the `user` object will be null hence you cannot read the UID. Do you want to add the document even after user logs out or only login? – Dharmaraj Jul 29 '21 at 16:34
  • I want to add document for new user or read document for existing user only when they are login. when there is no user I have login/signup screen rendered. I don't need file when there is no user logged in – Klak031 Jul 29 '21 at 16:38

1 Answers1

1

The onAuthStateChanged observer will trigger when the user logs in or logs out. In case the user has logged out, the user object will be null and hence you will get an error "TypeError: Cannot read property 'uid' of null". You should check if the user is still logged in inside of the auth observer.

const unsubscribe = auth.onAuthStateChanged(async (user) => {
  // Check if user is present (logged in) or absent (logged out)
  if (!user) {
    // user has logged out
    console.log("No User")
  } else {
    // Add the required documents
    setCurrentUser(user);

    const userDoc = db.collection("users").doc(user.uid);
    const doc = await userDoc.get()
    if (doc.exists) {
      setCurrentUserDoc(doc.data());
    } else {
      await userDoc.set({
        email: user.email,
        first_name: "",
        last_name: "",
        country: "",
        organization: "",
      });
    }
  }
})
Dharmaraj
  • 47,845
  • 8
  • 52
  • 84
  • is still not working now is working without user but not when i log in. i am getting error now `Unhandled Rejection (TypeError): doc.set is not a function` – Klak031 Jul 29 '21 at 16:46
  • 1
    @Klak031 my bad I just copied the code from question, it should be `userDoc.set(...)` – Dharmaraj Jul 29 '21 at 16:47
  • now is working without user but when i log in `TypeError: Cannot read property 'email' of null` inside component where i trying to use it. I added code where I trying to use to question – Klak031 Jul 29 '21 at 16:51
  • @Klak031 try `console.log(user)` and share the output please. – Dharmaraj Jul 29 '21 at 16:52
  • Sorry is working no problem. I made typo only thing is now i am getting this in console `an't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.` – Klak031 Jul 29 '21 at 16:55
  • I'm not sure about ReactJS issue but I think I found a similar issue which can be helpful: https://stackoverflow.com/questions/54954385/react-useeffect-causing-cant-perform-a-react-state-update-on-an-unmounted-comp – Dharmaraj Jul 29 '21 at 17:02
  • And now sometimes I need to refresh page manually after login to get it render – Klak031 Jul 29 '21 at 17:08
  • @Klak031 if you are referring to user's data not being shown on screen then you need to add `setCurrentUserDoc(doc.data())` in that else block as well... If the document is being added first time, you have no code that updates the state after adding the document. – Dharmaraj Jul 29 '21 at 17:09