firebase: Cannot assign to read only property 'displayName' of object '#<Object>' - reactjs

I'm using #reduxjs/toolkit: ^1.7.1 , firebase: 9.6.1 and firebase-tools: 10.0.1 on my ReactJS project.
I was trying to create a function where users can update their names and avatar photos.
For which I used updateProfile() function. But whenever I execute the update function it threw an error Cannot assign to read only property 'displayName' of object '#<Object>'.
There is an interesting thing I have noticed that is if I only update the photoURL property still it gives Cannot assign to read only property 'displayName' of object '#<Object>'
Project_Link_Github
Code: useFirebase.js:
import { getAuth, signInWithPopup, GoogleAuthProvider, createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut, onAuthStateChanged, updateProfile } from 'firebase/auth';
import { getDownloadURL, getStorage, ref, uploadBytes } from 'firebase/storage';
import { useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useLocation, useNavigate } from 'react-router-dom';
import { setIsLoading } from '../features/isloadingSlice';
import { login, logout } from '../features/userSlice';
import initAuth from '../Firebase/initAuth';
initAuth();
export const useFirebase = () => {
const auth = getAuth();
const location = useLocation();
const navigate = useNavigate();
const dispatch = useDispatch();
const [updateCount, setUpdateCount] = useState(0);
const storage = getStorage();
const Redirect = () => {
console.log(location);
const destination = location?.state?.from?.pathname || '/';
navigate(destination);
}
const uploadAvatar = async (file) => {
const fileRef = ref(storage, 'avatar/' + auth?.currentUser?.uid + '.png');
dispatch(setIsLoading(true));
const snapshot = await uploadBytes(fileRef, file);
const photoURL = await getDownloadURL(fileRef);
updateProfile(auth.currentUser, { photoURL }).then(() => {
setUpdateCount(updateCount + 1);
}).catch(e => console.log(e.message))
dispatch(setIsLoading(false));
console.log(snapshot);
}
const userRegister = (name, photoURL, email, password) => {
dispatch(setIsLoading(true));
createUserWithEmailAndPassword(auth, email, password)
.then(result => {
updateProfile(auth.currentUser, {
displayName: name, photoURL
}).then(() => { })
dispatch(login({ displayName: name, email, photoURL }));
Redirect();
}).catch(error => alert(error.message))
.finally(() => dispatch(setIsLoading(false)))
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (result) => {
if (result) {
dispatch(login({ ...result }))
}
else {
dispatch(login({}))
}
dispatch(setIsLoading(false));
})
return () => unsubscribe;
}, [updateCount, auth])
return {
logIn,
logOut,
Redirect,
uploadAvatar,
userRegister,
}
}
I don't know what's wrong with this displayName property but my previous project works fine.
Can anybody please help me with this?

I have 2 different projects using authentication of firebase, both using updateProfile() of firebase/auth. One of them works fine, but the other, which use #reduxjs/toolkit: ^1.8.4 got the same problem as yours, so i believe that it has something to do with reduxjs/toolkit. However, the function actually does work, the data has updated on server side (firebase). So in my case, i just ignore it.

Related

UseContext returns null in getServer side props NextJS

I have used the context in other places, such as login, database functions, and more. However, when I try to run functions or variables inside my context in places such as custom api's or getServerSideProps, it returns the following error, TypeError: Cannot read properties of null (reading 'useContext'). I am attaching my auth context, my initialization of the context, and the getServerSideProps function that is returning an error
_app.js
import RootLayout from '../components/Layout'
import { AuthProvider } from '../configs/auth-context'
import '../styles/globals.css'
export default function App({ Component, pageProps }) {
return (
<AuthProvider >
<RootLayout>
<Component {...pageProps} />
</RootLayout>
</AuthProvider>
)}
auth-context
import React, { useContext, useState, useEffect, useRef } from 'react'
import { auth, db, provider } from './firebase-config'
import { GoogleAuthProvider, signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut, onAuthStateChanged, signInWithPopup } from 'firebase/auth'
import { doc, getDoc, setDoc } from 'firebase/firestore'
import {useRouter} from 'next/router';
const AuthContext = React.createContext({currentUser: {uid: "TestUid", email:"Testeremail#email.com"}})
export function UseAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const router = useRouter();
const [currentUser, setCurrentUser] = useState({uid: "TestUid", email:"Testeremail#email.com"})
const [loading, setLoading] = useState(true)
async function signup(email, password) {
createUserWithEmailAndPassword(auth, email, password)
.then(async (result) => {
const user = result.user;
await userToDb(user);
router.push('/portfolio');
return user;
}).catch((error) => {
console.error(error);
})
return
}
async function login(email, password) {
return signInWithEmailAndPassword(auth, email, password)
.then(async (result) => {
const user = result.user;
await userToDb(user);
router.push('/portfolio');
return user;
}).catch((error) => {
console.error(error)
})
}
function logout() {
router.push('/')
return signOut(auth)
}
async function googleSignIn() {
const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
.then(async (result) => {
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
// The signed-in user info.
const user = result.user;
await userToDb(user);
router.push('/portfolio');
return user
}).catch((error) => {
console.log(error)
// const errorCode = error.code;
// const errorMessage = error.message;
// The email of the user's account used.
// const email = error.customData.email;
// The AuthCredential type that was used.
// const credential = GoogleAuthProvider.credentialFromError(error);
} )
}
const userToDb = async (user) => {
// await setDoc(doc(db, "users", user.uid), {
// userEmail: user.email,
// userID: user.uid
// }, {merge: false})
let currentRef = doc(db, 'users', user.uid)
let currentUserID = user.uid;
let currentEmail = user.email;
await setDoc(currentRef, {
userEmail: currentEmail,
userID: currentUserID
}, {merge: false})
}
function fixData(docs) {
console.log("this works")
// setDocuments(docs);
let retMap = new Map();
if (currentUser !== null) {
docs?.map(function(doc) {
console.log(doc)
let tic = doc.stockTicker
let data = {
shares: doc.shares,
price: doc.price,
type: doc.type
}
if(!retMap.has(tic)) {
retMap.set(tic, [data]);
console.log(tic + " " + data)
// setMap(new Map(datamap.set(tic, {shares: shares, averagePrice: price})))
}
else {
let x = retMap.get(tic);
x.push(data);
}
})
console.log(retMap)
return retMap;
}
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
const value = {
currentUser,
login,
signup,
logout,
googleSignIn,
fixData
}
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
)
}
getServerSideProps
export async function getServerSideProps() {
let allDocs = []
let avgDocs = []
const {currentUser} = UseAuth()
return {
props: {allDocs, avgDocs}
}
}
I don't know the correct answer, but hooks should be used in components and hooks without exception to ssr.

Blank page after refresh react firebase authentication

I have the above firebase db. I want to extract the displayName value and use it in a greeting message after the user is successfully login (e.g. Hello George!). I manage to achieve this but when I refresh the page everything disappears and in console I get this error "index.esm2017.js:1032 Uncaught TypeError: Cannot read properties of undefined (reading 'indexOf')".
Is this a problem of how I extract the displayName from firebase document?
Can someone explain to me what is the problem, please?
Here is my code:
AuthContext.js
import { createContext, useContext, useEffect, useState } from "react";
import {
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
} from "firebase/auth";
import { auth } from "../utils/firebase/firebase.utils";
const UserContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState({});
const signIn = (email, password) =>
signInWithEmailAndPassword(auth, email, password);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
console.log(currentUser);
setUser(currentUser);
});
return () => unsubscribe();
}, []);
const logOut = () => signOut(auth);
return (
<UserContext.Provider value={{ user, signIn, logOut }}>
{children}
</UserContext.Provider>
);
};
export const UserAuth = () => {
return useContext(UserContext);
};
WelcomePage.jsx
import React, { useState, useEffect } from "react";
import { UserAuth } from "../contexts/AuthContext";
import { db } from "../utils/firebase/firebase.utils";
import { doc, Firestore, getDoc } from "firebase/firestore";
const WelcomePage = () => {
const [userDetails, setUserDetails] = useState({});
const { user } = UserAuth();
useEffect(() => {
const docRef = doc(db, "users", user.uid);
const fetchData = async () => {
try {
const docSnap = await getDoc(docRef);
setUserDetails(docSnap.data());
console.log(docSnap.data());
} catch (e) {
console.log(e);
}
};
fetchData();
}, [user]);
return (
<div>
<h1>Hello, {userDetails.displayName}!</h1>
</div>
);
};
export default WelcomePage;
You might only want to fetch documents in the WelcomePage component if there's a truthy uid value to use.
const { user } = UserAuth();
useEffect(() => {
const fetchData = async () => {
const docRef = doc(db, "users", user.uid);
try {
const docSnap = await getDoc(docRef);
setUserDetails(docSnap.data());
console.log(docSnap.data());
} catch (e) {
console.log(e);
}
};
if (user?.id) {
fetchData();
}
}, [user]);

"Objects are not valid as a React child" when adding user data to Firestore

I want to know that how can we save user data during signup in Firestore.
import {
createUserWithEmailAndPassword,
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
} from "firebase/auth";
import { collection, setDoc, doc } from "firebase/firestore";
import React, { useContext, useEffect, useState } from "react";
import { auth, db } from "../firebaseConfig";
const AuthContext = React.createContext();
export const useAuth = () => {
return useContext(AuthContext);
};
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
const signup = async (email, password) => {
return createUserWithEmailAndPassword(auth, email, password).then(
(cred) => {
setDoc(doc(collection(db, "users", cred.user.uid)), {
name: "manan",
city: "chd",
});
}
);
};
const login = (email, password) => {
return signInWithEmailAndPassword(auth, email, password);
};
const logout = () => {
return signOut(auth);
};
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
};
as you can see I have created an AuthContext.js for all the functions. now, when in signup() I am not able to store the user data corresponding to their UID... as I have used
setDoc(doc(collection(db, "users", cred.user.uid)), {
name: "manan",
city: "chd",
});
but it is not working at all it's throwing an error
error:
Objects are not valid as a React child (found: object with keys {error}). If you meant to render a collection of children, use an array instead.
you are using both a document and a collection as a reference. you should only use a document reference on a setDoc
from:
setDoc(doc(collection(db, "users", cred.user.uid)), {
name: "manan",
city: "chd",
});
change to:
setDoc(doc(db, "users", cred.user.uid), {
name: "manan",
city: "chd",
});

How to store download urls and retrieve them from a collection

I have a form that sends data and images to firebase (firestore). I created a collection that only stores the urls. What I need is a way to query the different images urls based on a document reference ID because in my hierarchy, the last collection creates documents with unique ID and I'm unable to query them in order to get the image url.
Form.js
import { useSelector } from "react-redux";
import { db, storage } from "../../firebase";
import {
addDoc,
collection,
doc,
updateDoc,
} from "#firebase/firestore";
import { getDownloadURL, ref, uploadBytes } from "#firebase/storage";
import { useSession } from "next-auth/react";
function Form() {
const { data: session } = useSession();
const Images = useSelector((state) => state.draggedImages.images);
const imageTarget = Images.length - 1;
const SendPost = async () => {
const docRef = await addDoc(collection(db, "posts"), {
id: session.user.uid,
AdDescription: description,
});
Images[imageTarget].map((Img) => {
const imageRef = ref(storage, `posts/${docRef.id}/${Img.name}`);
uploadBytes(imageRef, Img, "data_url").then(async () => {
const downloadURL = await getDownloadURL(imageRef);
await updateDoc(doc(db, "posts", docRef.id), {
image: downloadURL,
});
// ---------------HERE IS THE PROBLEM--------------
await addDoc(collection(db, "ImageUrl", docRef.id, "Urls"), {
image: downloadURL,
});
// --------------------------------------------------
});
});
};
}
export default Form;
upon uploading the images, I have to fetch them into a carousel.
Carousel.js
import {
collection,
doc,
onSnapshot,
orderBy,
query,
getDocs,
} from "#firebase/firestore";
import { useRouter } from "next/router";
import React, { useEffect, useRef, useState } from "react";
import { db } from "../../firebase";
function Carousel() {
const [FetchedImages, setFetchedImages] = useState([]);
const router = useRouter();
const { id } = router.query;
useEffect(
() =>
onSnapshot(doc(db, `ImageUrl/${id}`), (snapshot) => {
setFetchedImages(snapshot.data());
}),
[db]
);
console.log("fetched : ", FetchedImages); // returns undefined
}
export default Carousel;
The defined hierarchy in the Form.js is pretty fine. The problem was actually the way to retrieve the data from Carousel.js using useEffect.
Following this resource , Here's the updated and working solution I used.
Carousel.js
useEffect(() => {
const FetchedImagesFromFirestore = async () => {
const querySnapshot = await getDocs(
collection(db, `ImageUrl/${id}/Urls`)
);
querySnapshot.forEach((doc) => {
setFetchedImages((prevState) => [...prevState, doc.data()]);
});
};
FetchedImagesFromFirestore();
}, [db]);

How to access firebase auth userMetadata

I'm trying to find a way to access the creationTime and lastSignInTime described in this documentation.
Are there any examples of using it within react hooks?
I can't make sense of the firebase documentation generally - it's just words on a page. I think it is designed for people who intuitively know how to fill in the blanks. I remain mystified as to how to do that in general.
I can access auth.user.email using a react hook as follows:
import React, { useState, useEffect, useContext, createContext } from "react";
import firebase from "../firebase";
import {auth} from "../firebase";
const authContext = createContext();
// Provider wraps app and makes auth object available by useAuth().
export function ProvideAuth({ children }) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}
// Hook to get the auth
export const useAuth = () => {
return useContext(authContext);
};
// Provider hook that creates auth state
function useProvideAuth() {
const [user, setUser] = useState(null);
const signin = (email, password) => {
return firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(response => {
setUser(response.user);
return response.user;
});
};
const signup = (email, password) => {
return firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(response => {
setUser(response.user);
return response.user;
});
};
const signout = () => {
return firebase
.auth()
.signOut()
.then(() => {
setUser(false);
});
};
const sendPasswordResetEmail = email => {
return firebase
.auth()
.sendPasswordResetEmail(email)
.then(() => {
return true;
});
};
const confirmPasswordReset = (code, password) => {
return firebase
.auth()
.confirmPasswordReset(code, password)
.then(() => {
return true;
});
};
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => {
if (user) {
setUser(user);
} else {
setUser(false);
}
});
return () => unsubscribe();
}, []);
return {
user,
signin,
signup,
signout,
sendPasswordResetEmail,
confirmPasswordReset
};
}
Now, I'm trying to figure out what I need to do to either access the string values described here or the timestamps described here.
I tried each of (all guesses):
{auth.user.UserMetadata().creationTime}
{auth.user.creationTime}
{auth.user.UserMetadata.creationTime}
This works.
{auth.user.metadata.creationTime}
I don't understand why. The references in the firebase documentation refer to metadata as UserMetadata. I don't know how to find the piece of information that tells people to make the leap between UserMetadata and metadata.
If anyone knows what the key to this is, I'd be forever grateful for the insight.

Resources