How to properly update user information in firebase? - javascript

I have a code that is a form, basically is suppose to update the user information.
According to Firebase documentation manage user but for some reason I'm getting this errors
This is what I have I don't think I'm missing anything is pretty much the same
The only difference is that on my firebase.js I made new cons to make codes shorter but that shouldn't affect at all.
firebase.js
const db = firebase.firestore();
const auth = firebase.auth();
const storage = firebase.storage();
export {db, auth, storage};
Code
import {auth} from "./firebase"
const authUser = auth.currentUser
const update = (e) => {
e.preventDefault();
authUser.updateProfile({
displayName: {fullName}
})
}
I just wanted to test it on the user name first and then do all the others but I don't understand why is not working is super simple.
Update

authUser is undefined. This means that auth.currentUser was undefined at the time when it was assigned to authUser. This probably means that the user is not logged in. You will need to handle the case when the user is not logged in, like
import {auth} from "./firebase"
const authUser = auth.currentUser
const update = (e) => {
e.preventDefault();
if (authUser) {
authUser.updateProfile({
displayName: fullName
})
} else {
//User is not logged in, handle that case here
}
}

import {auth} from "./firebase";
const authUser = auth.currentUser;
const update = (e) => {
e.preventDefault();
authUser?.updateProfile({
displayName: fullName
});
}

Related

React & Firebase Global State

I'm using react-firebase-hook , and I'm trying to check if the user is admin or not, and I want to it to be a global state where I don't have to add this code in every and each component to check if the user is admin or not, here is the code..
import { useState, useEffect } from 'react';
import { query, collection, getDocs, where } from "firebase/firestore";
import { auth, db } from "../../config/fbConfig";
import { useAuthState } from "react-firebase-hooks/auth";
const CreateAnn = () => {
const [ann, setAnn] = useState(''); // ignore this
const [admin, setAdmin] = useState(false);
const [user] = useAuthState(auth);
const fetchAdmin = async () => {
try {
const q = query(collection(db, "users"), where("uid", "==", user?.uid));
const doc = await getDocs(q);
const data = doc.docs[0].data();
if(data.admin === true) {
setAdmin(true);
}
else { setAdmin(false); }
} catch (err) {
// do nothing
}
};
useEffect(() => {
fetchAdmin();
});
I want to have this as a global state, tried to useContext but i think I'm using it the wrong way, so anyone can help?
You are correct to use a context, however, you might use it wrong as you said.
You should set up a context that handles the currently logged in user.
In this context you can also fetch the extra details of the user from the user collection.
Also, you can grab the user directly with ID instead of where:
const docRef = doc(db, "users", user.uid);
const docSnap = await getDoc(docRef);
const data = docSnap.exists ? docSnap.data() : undefined
Follow this link to set up the context of auth correct.
https://dev.to/dchowitz/react-firebase-a-simple-context-based-authentication-provider-1ool

Uncaught ReferenceError: getAuth is not defined

Self taught coder here. Hopefully I'm explaining the issue adequately.
I'm trying to create some user authentication using firebase. I keep getting an error saying "Line 18:16: 'getAuth' is not defined". I'm confused because I was following a tutorial. I have tried reordering my imports as I read online that might be the reason for the error. For some reason I think my problem is in the config file and how I've initialized everything. I'm new to firebase. Any potential solves would be appreciated.
Here is my firebase.js config
import { initializeApp } from "firebase/app";
import { getFirestore } from 'firebase/firestore'
import { getAuth } from "firebase/auth";
import "firebase/storage"
const firebaseConfig = {
apiKey: "process.env.REACT_APP_FIREBASE_KEY",
authDomain: "uploadimg.firebaseapp.com",
projectId: "uploadimgofficial",
storageBucket: "uploadimg.appspot.com",
messagingSenderId: "MESSENGER_ID",
appId: "APP_ID",
measurementId: "MESAUREMENT_ID"
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth()
// Init firestore
const db = getFirestore()
export { db }
And this is where I'm trying to set up the login page
import React from "react";
import "./Login.scss";
import { useState } from "react";
import { signInWithEmailAndPassword } from "firebase/auth";
import { auth } from "../../firebase";
const Login = () => {
const [error, setError] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleLogin = (e) => {
e.preventDefault();
};
const auth = getAuth(); // <------ THIS IS ERROR LINE
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
console.log(user)
})
.catch((error) => {
setError(true)
});
return (
<div className="login">
<form onSubmit={handleLogin} className="login__form">
<input
className="login__email"
onChange={e => setNewEmail(e.target.value)}
type="email"
placeholder="email" />
<input
The issue with your code is that you have imported the getAuth function incorrectly.
To fix this, you need to correct the name usage when calling the function.
As you have imported getAuth as auth in the import statement at the top of your file, you need to use it as so.
import { auth } from "../../firebase.js";
Right now, you are importing the auth variable, which is set to the return value of getAuth, as defined below in the firebase.js file.
// As you are calling the function with '()', you are getting the return value.
export const auth = getAuth();
To fix this, simply change your function call to auth. Also, rename the variable to something other than auth to avoid name collisions, and/or confusing naming patterns.
Also, as you are getting and setting the return value of getAuth to auth, the return value may not be a function1. In that case, you can't call auth with brackets (()), as, for instance, it may return an object.
// Change the variable name to what you want.
const userAuth = auth;
To check if it is a string/object/function/etc, you can use typeof (for checking/debugging; remove this line once done).
console.log(typeof auth); // Should return: 'string' | 'function' | 'object' | ...
Depending on the return type, you can change your usage to match it.
In conclusion, to fix your issue, you need to correctly use the name (auth instead of getAuth). Also, make sure to check the return value of getAuth, and use it appropriately!
1 Please correct me in the comments if I am incorrect; the return value is a function. In that case, I can remove that part. Thank you for the clarification!

Is there a way to make this asynchronous?

This seems like an easy one and that I'm just missing something obvious, but a little background:
I am making a mock "bug reporting" web app. The app has two types of user: "user" and "engineer". Upon signup the user is assigned a type, and it's saved in a firebase collection "users" under a firebase unique identifier "uid".
Upon login to the web app, a firebase user object for the logged in user is retrieved from firebase. This object has "user.uid" on it.
In order to grab the user type (saved as userType in the firebase document) I need to take that user.uid and send the uid part to a react hook I have made. This hook then fetches the document with that uid and returns the userType.
I've made the following dummy file to demonstrate.
import {React, useEffect, useState} from 'react'
import { useAuthContext } from '../../hooks/useAuthContext'
import { useDocument } from '../../hooks/useDocument'
export default function User() {
const { user } = useAuthContext();
console.log("uid: " + user.uid)
const id = user.uid;
console.log("id: " + id)
let { document, error } = useDocument("users", id)
console.log("userType: " + document.userType)
return (
<div>
</div>
)
}
Now the problem I have is that "user" isn't initalised from Context in time before the program tries to go fetch it, using a uid that, again, isn't initialised in time.
Basically I just need a way to delay using the useDocument hook, but I can't make hooks asynchronous. My async skills aren't my strongest point, to say the least.
Any help is massively appreciated, I've spent so many hours trying to crack this.
EDIT: Upon request, here is the useDocument hook:
import { useEffect, useState } from "react"
import { projectFirestore } from "../firebase/config"
export const useDocument = (collection, id) => {
const [document, setDocument] = useState(null)
const [error, setError] = useState(null)
// realtime document data
useEffect(() => {
const ref = projectFirestore.collection(collection).doc(id)
const unsubscribe = ref.onSnapshot(snapshot => {
// need to make sure the doc exists & has data
if(snapshot.data()) {
setDocument({...snapshot.data(), id: snapshot.id})
setError(null)
}
else {
setError('No such document exists')
}
}, err => {
console.log(err.message)
setError('failed to get document')
})
// unsubscribe on unmount
return () => unsubscribe()
}, [collection, id])
return { document, error }
}

React doesn't call useEffect on firebase (v9) auth update

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);
}
};
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) => { ... }
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.
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

Firebase createUserWithEmailAndPassword not doing anything in Next.js

I am trying to use Firebase Authentication on my Next.js website. For some reason, when I press the sign up button, nothing happens and no errors are logged. It just refreshes the page. It doesn't even set any cookies or create a user.
Here are my two files related to authentication:
utils/authProvider.js:
import firebase from 'firebase/app';
import 'firebase/auth';
if(!firebase.apps.length) {
firebase.initializeApp({
// config
});
}
const auth = firebase.auth();
module.exports = { auth };
pages/signup.js:
import { useState } from 'react'
import { LockClosedIcon } from '#heroicons/react/solid'
import { auth } from '../utils/authProvider'
export default function CreateAccount() {
const [emailField, setEmailField] = useState('');
const [passwordField, setPasswordField] = useState('');
// emailField and passwordField are set correctly, I used console.log to test it
const createAccount = () => {
// this event does get triggered, I used console.log to test it
auth.createUserWithEmailAndPassword(emailField, passwordField)
.then((userCredential) => {
window.location.replace('/');
console.log('logged in!');
}).catch((error) => {
console.error(error);
});
}
Edit: After experimenting some more, I saw that the request to https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser never gets completed. It just shows as red in the network traffic tab of Chrome dev tools. Any reason why this might happen?
Thanks in advance.

Categories