Unable to retrieve document ID from Firebase query in useEffect function - javascript

I have the following function set up and it's sending data to "userInfo" correctly, but how do I access the value of "id" to use in another function?
const [userInfo, setUserInfo] = useState("");
const userID = auth.currentUser.uid;
//function for automatically retrieving user
useEffect(() => {
const q = query(collection(db, 'users'), where("uid", "==", userID));
onSnapshot(q, (querySnapshot) => {
setUserInfo(querySnapshot.docs.map(doc => ({
id: doc.id,
data: doc.data()
})))
})
console.log(userInfo);
}, [])
Keep in mind "uid" is different than "id". "id" is the document ID and "uid" is a value stored within the document.
When I run console.log(userInfo) it gives me an array of objects. The only thing I need to access is the value of "id".
I tried doing console.log(userInfo[0].id) and this works until I reload the page and then I get this error:
Uncaught TypeError: Cannot read properties of undefined (reading 'id')

You initialize your useState to an empty string "", and you change it inside a useEffect. Since useEffect fires once the page has rendered, you will at somepoint console.log(userInfo[0].id) while your useState is still a string
What happens when you try to access userInfo[0].id if userInfo is a string? You are trying to access something undefined
if you console.log the following with will not crash:
console.log(userInfo[0]?.id) //the **?** checks if the obj has the following prop

Related

Firebase saying arrayUnion was called with invalid data

Im getting an error with firebase because im trying to update two values when I press handleSelect. Only issue is that the first updateDoc works fine as I'm trying to add an array into the "user" specific userChats database, but when I try to do the opposite and add the user array to the "chat" database, it fails.
const handleSelect = async(chat) =>{
const docRef = doc(db, 'users', user?.uid)
const docSnap = await getDoc(docRef)
const addRef = doc(db, 'userChats', user?.uid)
await updateDoc(addRef, {
userChats: arrayUnion(chat)
})
const addRecieverRef = doc(db, 'userChats', chat?.uid)
await updateDoc(addRecieverRef, {
userChats: arrayUnion(user)
})
console.log(chat.uid)
const concatUID = user.uid > chat.uid ? user.uid + chat.uid : chat.uid + user.uid;
if(!docSnap.exists() && user.uid!=chat.uid){
await setDoc(doc(db, 'messages', concatUID), {
messages: [],
})
}
else{
dispatch({type: 'CHANGE_USER', payload: chat})
console.log(chat)
}
}
Error
Chats.js:53 Uncaught (in promise) FirebaseError:
Function arrayUnion() called with invalid data.
Unsupported field value: a custom UserImpl object (found in document userChats/lJ4u4PqWynXAPthz3FVgYaQQ0Do1)
I already checked and all the reference values are correct, and both "user" and "chat" are plain objects
Firestore can only store values of the types indicated in its documentation on data types. The UserImpl object that you are trying to store is not of a supported type, which is what the error message indicates.
If the user object comes from Firebase Authentication, you can call toJSON() on it to get a JSON serialization of its data.

Why changes made in database not updates in realtime?

I am trying to learn firestore realtime functionality.
Here is my code where I fetch the data:
useEffect(() => {
let temp = [];
db.collection("users")
.doc(userId)
.onSnapshot((docs) => {
for (let t in docs.data().contacts) {
temp.push(docs.data().contacts[t]);
}
setContactArr(temp);
});
}, []);
Here is my database structure:
When I change the data in the database I am unable to see the change in realtime. I have to refresh the window to see the change.
Please guide me on what I am doing wrong.
Few issues with your useEffect hook:
You declared the temp array in the way that the array reference is persistent, setting data with setter function from useState requires the reference to be new in order to detect changes. So your temp array is updated (in a wrong way btw, you need to cleanup it due to now it will have duplicates) but React is not detectign changes due to the reference to array is not changed.
You are missing userId in the dependency array of useEffect. If userId is changed - you will continue getting the values for old userId.
onSnapshot returns the unsubscribe method, you have to call it on component unMount (or on deps array change) in order to stop this onSnapshot, or it will continue to work and it will be a leak.
useEffect(() => {
// no need to continue if userId is undefined or null
// (or '0' but i guess it is a string in your case)
if (!userId) return;
const unsub = db
.collection("users")
.doc(userId)
.onSnapshot((docs) => {
const newItems = Object.entries(
docs.data().contacts
).map(([key, values]) => ({ id: key, ...values }));
setContactArr(newItems);
});
// cleanup function
return () => {
unsub(); // unsubscribe
setContactArr([]); // clear contacts data (in case userId changed)
};
}, [userId]); // added userId

Uncaught TypeError: Cannot read properties of undefined (reading 'question'). This is after using useState() to set the data from a GET request

I am making a simple GET request and want to make that data more accessible using useState() but it seems as though this error is caused by accessing an property that does not exist due to useState not updating it?
Even though I have made GET requests very similar to this, it is the first time I am using useLocation(). I'm not sure if that has anything to do with the problem or it has something to do with useState().
Any response is much appreciated
const getQuiz = async () => {
try{
// These values were passed by the difficulty Component
const categoryName = location.state?.name
const difficulty = location.state?.difficulty
// This makes a get request to get data for the quiz
let response = await axios.get(`https://the-trivia-api.com/api/questions?categories=${categoryName}&limit=10&difficulty=${difficulty}`)
let arrayDataResponse = await response.data
// This sets the data to question array so that it is accessible outside of this function
setQuestionArray(arrayDataResponse)
// this outputs an empty array
console.log(questionArray)
} catch(err){
console.log(err)
}
}
// This fetches the data on mount
useEffect(() => { getQuiz() }, [])
// This will set the data for the elements once the state of the question array has been set from the get request
useEffect(() => {
// This sets the content for the question element
setQuestion(questionArray[0].question)
// <h4>{question}</h4>
// Uncaught TypeError: Cannot read properties of undefined (reading 'question')
}, [questionArray])
I'm guessing that your state is defined something like this...
const [questionArray, setQuestionArray] = useState([]);
const [question, setQuestion] = useState(/* some initial value */);
This means that when your component is initialised and mounted, questionArray is an empty array.
Effect hooks not only execute when their dependencies change but also when they are initialised. That means when this hook first runs...
useEffect(() => {
setQuestion(questionArray[0].question);
}, [questionArray]);
It's trying to access .question on undefined, hence your error.
I would skip the question state and the above hook entirely. If you want something to represent the optional first question, you can use a memo hook instead
const firstQuestion = useMemo(() => questionArray[0]?.question, [questionArray]);
or simply use questionArray[0]?.question directly without any hooks.
This will either return the first question property or undefined which you can detect using conditional rendering
{firstQuestion && (
<p>{firstQuestion}</p>
)}
{/* or */}
{questionArray.length > 0 && (
<p>{questionArray[0].question}</p>
)}
const getQuiz = async () => {
try{
// These values were passed by the difficulty Component
const categoryName = location.state?.name
const difficulty = location.state?.difficulty
// This makes a get request to get data for the quiz
let response = await axios.get(`https://the-trivia-api.com/api/questions?categories=${categoryName}&limit=10&difficulty=${difficulty}`)
let arrayDataResponse = await response.data
// This sets the data to question array so that it is accessible outside of this function
setQuestionArray(arrayDataResponse)
//Solution
// set question from here
setQuestion(arrayDataResponse[0].question)
// this outputs an empty array
console.log(questionArray)
} catch(err){
console.log(err)
}
}
// then you don't need to run useEffect for this , Your state will be fiilled with api response
// not required code below
useEffect(() => {
// This sets the content for the question element
setQuestion(questionArray[0].question)
// <h4>{question}</h4>
// Uncaught TypeError: Cannot read properties of undefined (reading 'question')
}, [questionArray])

Trying to store FireStore array in React Native?

I have been trying to push or store a FireStore array in one of my own arrays. I have tried a few versions of code, the first being this:
var data = [];
db.collection('Rooms')
.doc(code)
.get()
.then((docs) => data.push(docs.data()));
However, when I log the data variable, it comes out as an empty array. The second method I have tried is this:
var [data, setData] = useState([]);
db.collection("Rooms")
.doc(code)
.get()
.then((docs) => setData(docs.data()));
However this method seems to setData infinitely, so it is reading into my API infinitely, which I would like to avoid. The last method I tried was this:
var data = db.collection("Rooms").doc(code).get();
console.log(data);
But this just returns
Promise {
"_U": 0,
"_V": 0,
"_W": null,
"_X": null,
}
Could anyone help me with this, ideally I'd like to store the data of an array called "MovieArray" inside the document, but I can't even access the document, so even if you can just help me store the data of the whole document, it would be very helpful.
If you are using react, I would suggest using the hook. You also, don't really need to push objects to an array like that.
Here is an example of how to get some data and store the collection of data.
const Forum = () => {
const [posts, setPosts] = useState(null);
const collectIdsAndDocs = (doc) => {
return { id: doc.id, ...doc.data() };
};
useEffect(() => {
const getPost = async () => {
const snapshot = await firestore.collection('Posts').get();
const myPosts = snapshot.docs.map(collectIdsAndDocs);
console.log(myPosts);
setPosts({ myPosts });
};
const createPost = async (post) => {
const docRef = await firestore.collection('Posts').add(post);
const doc = await docRef.get();
console.log(doc);
};
createPost({ Title: 'My First Post', Content: 'My content' });
getPost();
}, []);
return (
// return some JSX
);
};
Why does this work?
When you get a collection, Firebase returns a snapshot of the collection.
This snapshot has a list of docs or an array if you will.
We then want to map over those docs constructing a new object that contains just the document data and the ID of individual doc. This is what the myPosts variable is.
Using the react state hook, you can set that object to the current state of Posts, in your case this would be rooms.
When you add something to the database, Firestore will return a reference to the newly added item. You can then call get() to get the document back if you need it.
Try changing to (see comment before this)
const [data, setData] = useState({});

Undefined is not an object while trying to query nested objects. Using axios and React

The JSON response is as shown in the image 1.
I was able to assign the entire response using axios (which already does the JSON.parse) to the state (named profile).
while profile.bio and profile.image are working;
profile.user.username, etc are not working and throwing an error - Undefined is not an object
const [profile, setProfile] = useState({});
const phone_no = phone.phone;
const fetchProfile = useEffect(() => {
var res = {};
axios
.get('<API URL>' + phone_no)
.then((response) => (res = response.data))
.then(() => {
setProfile(res);
})
.then(() => console.log(profile))
.catch((e) => console.log(e)); });
const user_stream = {
name: first.first_name,
image: profile.image,
id: profile.user.id,
};
Update - Solution: Using async-await with axios, it's fixed.
profile or profile.user may still be undefined when trying to access it, so profile.bio is just undefined so it doesn't cause an error, but profile.user.username tries to access a property of an undefined object.
Try adding profile?.user?.username
Or profile && profile.user && profile.user.username
This will ensure that it only tries to render the username if profile is already defined

Categories

Resources