1

I'm uploading a user profile image into firebase storage, then updating the photoURL value of the auth user with updateProfile(). After that I want the user to be updated without manually refreshing the page. I've been trying this for days now and the issue gets more weird every time I try to debug it.

The interesting thing is the user object seems to be already updated when I log it with console.log(currentUser) after the then promise of updateProfile() is fulfilled. So the new photoURL is already present in the currentUser object. But it seems to not call a state update or console.log("!!!!currentAuthUserUpdate", user);. So the user image wouldn't refresh in my page.

I even tried it with doing a useEffect with the currentUser as a dependency but it wasn't fired. Still, the currentUser object changed when logging it after updateProfile()

Updating the profile, UpdateUserImage.tsx:

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

const { currentUser } = useAuth();

// updating the user profile
updateProfile(currentUser, { photoURL })

AuthContext.tsx:

import { auth } from "./../firebase/firebase";

const [currentUser, setCurrentUser] = useState(null);

const auth = getAuth(app);

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((user) => {
      console.log("!!!!currentAuthUserUpdate", user);

      // I tried setting the user as a custom new object: const userData = { ...user };
      setCurrentUser(user);
    });
    return unsubscribe;
  }, []);

firebase.js

import { getAuth } from "firebase/auth";

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

export {
  auth
};

What I tried additionally: (But this wouldn't work as the user is already updated without a state refresh from react, so it's trying to replace the same object)

const reloadUser = async () => {
    try {
      const res = await currentUser.reload();
      const user = auth.currentUser;
      console.log("currentUser:", user);

      setCurrentUser(auth.currentUser);

      console.log("res", res);
    } catch (err) {
      console.log(err);
    }
  };
BenjaminK
  • 653
  • 1
  • 9
  • 26
  • Can you please provide the code of `getAuth` hook? – Nik Feb 07 '22 at 17:11
  • Does this answer your question? [How do I use the Firebase onAuthStateChange with the new React Hooks?](https://stackoverflow.com/questions/55366320/how-do-i-use-the-firebase-onauthstatechange-with-the-new-react-hooks) – Nik Feb 07 '22 at 17:31
  • @Nik I added it. The `getAuth` hook comes from the firebase SDK. And no your link doesn't answer my question as I already know how to handle `onAuthStateChanged` in react functional components. – BenjaminK Feb 07 '22 at 17:39

3 Answers3

1

it's not auth.onAuthStateChanged. You need to import onAuthStateChanged from 'firebase/auth'

import { getAuth, onAuthStateChanged } from "firebase/auth"
const auth = getAuth(); // leave getAuth empty if you only have one app

Then in your useEffect it should be

 onAuthStateChanged(auth, async (currentUser) => { ... }
Someone Special
  • 12,479
  • 7
  • 45
  • 76
  • Thank you for your answer. You're right I wasn't using the v9 functional approach there. I changed that in my code. After changing that the code runs exactly like before. The page is sadly still not refreshing even if the `currentUser` has new data in it through the `user` change. `console.log("!!!!currentAuthUserUpdate", user);` is also not logged after the update. – BenjaminK Feb 08 '22 at 09:08
  • You need to call `auth.currentUser.reload()` after you update the user. If you are using custom claims, then you also need to call `currentUser.getIdTokenResult(true)`. You need to go google up this 2 functions. – Someone Special Feb 08 '22 at 10:52
  • onAuthChanged is only going to be call once when you logged in, and when you call `getIdTokenResults(true)` – Someone Special Feb 08 '22 at 10:53
  • Yes I tried that already, I'm having a function for it that I call after the `updateProfile` but still it isn't updating the state currentUser. Here my function: ```const reloadUser = async () => { if (auth.currentUser) { const res = await auth.currentUser.reload(); currentUser.getIdTokenResult(true); const tempUser = auth.currentUser; // console.log("currentUser:", tempUser); setCurrentUser(tempUser); } };``` – BenjaminK Feb 08 '22 at 12:37
  • What are u updating? name? profile? custom claim? – Someone Special Feb 08 '22 at 13:28
  • In this case only the `photoURL`. As this functionality is when a user uploads a new profile photo. The navbar is another component dependent on the global `currentUser`state which it gets from my AuthContext and there I use the `currentUser.photoURL`. – BenjaminK Feb 08 '22 at 15:08
  • 1
    probably try `setCurrentUser({ ...tempUser})` instead. – Someone Special Feb 08 '22 at 15:46
  • I now got away with giving the profileURL image in the navbar a ``and setting the current user with a copy of the old user `setCurrentUser({ ..tempUser})`. I also fixed the syntax to the v9 functional approach. This is part of the solution and part of a workaround. But I'm very thankful for at least getting away with that for now. – BenjaminK Feb 10 '22 at 14:44
0

The setCurrentUser function returned from useState isn't always the same function in my experience.

You can try passing it as a dependency into the useEffect - but I don't think that's what you want.

React lets you use an old useState setter if you give it an updater function, rather than a value: setCurrentUser(()=>auth.currentUser)

The React docs dispute this though.

Will S
  • 744
  • 8
  • 17
-1

Using useStates are good for re-rendering components. However going into utilizing useRefs are best for updating the actual variable and will not cause a re-render of the component.

Declare it like:const currentUser = useRef(null)

Update it like: currentUser.current = updatedUserData

Use in code like: currentUser.current.photoURL

Eman S
  • 9
  • So you're saying I shouldn't use `setState` to store the user instead should use a Ref to store it? Hm I've seen the documentation and multiple threads using `useState` to store the`currentUser`. Can you go into detail why `useRef` instead of `useState`and how to set it then? The docs also say the user obj should only be refreshed when signing in and out not when updating the user, which is super weird as it's doing it in my case. [Documentation](https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user) – BenjaminK Feb 07 '22 at 16:54
  • 1
    This won't cause the component to re-render, which would be the goal of using `useState` in the first place. – Will S Feb 07 '22 at 17:12
  • Correct I did state that the component will not render. However if the issue is not setting/updating the state then useRef would be more practical. – Eman S Feb 07 '22 at 17:24
  • The issue is that the state value update isn't rerendering the component value or calling the `useEffect` dependency. I now tried to write everything to a `useRef` instead of a `useState` but it sadly still didn't work, instead it caused other issues. – BenjaminK Feb 07 '22 at 20:12