How to get document id of firestore in React - javascript

This is my firestore database:
After useEffect() updated the workouts const, I have access to name and uId only. How can I have access to the document id as well?
import React, { useState, useEffect } from "react";
import "./Workouts.sass";
import Workout from "./Workout";
import firebase from "./firebase";
function Workouts() {
const [workouts, setWorkouts] = useState([]);
useEffect(() => {
let user = firebase.auth().currentUser;
const database = firebase.firestore();
const unsubscribe = database
.collection("workouts")
.where("uId", "==", user.uid)
.onSnapshot((snapshot) => {
setWorkouts(snapshot.docs.map((doc) => doc.data()));
});
return () => {
unsubscribe();
};
}, []);
return (
<div className="Workouts">
{workouts.map((workout) => (
<Workout key={workout.name} workout={workout} />
))}
</div>
);
}
export default Workouts;

You can use the snapshot doc.id
I like to add id as a property to the object:
snapshot.docs.map((doc) => ({id: doc.id, ...doc.data()}))

Related

Problem adding my firebase project to my react project

So Im making an ecommerce react app, and decided to integrate firebase database to my project. I already created my first project on firebase and loaded all my products there with a random ID.
I imported firebase into my project. Problem is that the general product container seems to be working good but when i do the detail container (which is my one product page view), everything breaks. I will leave the code to each sections in which i implemented firebase database.
This is my cardliscontainer which seems to be working fine
import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import productos from "../../utils/productsMock";
import CardList from "../CardList/CardList";
import LinearProgress from '#mui/material/LinearProgress';
import Box from '#mui/material/Box';
import "./CardListContainer.css";
//Firestore
import { collection, getDocs } from "firebase/firestore";
import db from '../../utils/firebaseConfig';
const CardListContainer = () => {
const [products, setProducts] = useState([]);
const [spinner, setSpinner] = useState(false);
//
const { idCategory } = useParams();
const getProductoss = async () => {
const productSnapshot = await getDocs(collection(db, "products"));
const productList = productSnapshot.docs.map((doc) => {
let product = doc.data()
product.id = doc.id
return product
})
return productList
}
const getProducts = () => {
setSpinner(true);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(productos);
}, 1000);
});
};
useEffect(() => {
getProductoss()
.then((response) => {
console.log("productos", response)
setSpinner(false);
setProducts(
idCategory
? response.filter(
(item) => item.category === idCategory
)
: response
);
})
.catch((err) => {
console.log("Fallo.");
});
}, [idCategory]);
return (
<>
{
spinner
?
<Box sx={{ width: '100%' }}>
<LinearProgress />
</Box>
:
<div className="general-container">
<CardList products={products} />
</div>
}
</>
)
};
export default CardListContainer;
This is my detail container which is gving me blank page
import React from "react";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import productos from "../../utils/productsMock";
import CardDetail from "../CardDetail/CardDetail";
import { doc, getDoc } from "firebase/firestore";
import db from "../../utils/firebaseConfig";
import "./CardDetailContainer.css";
const CardDetailContainer = () => {
/* DEFINIR PARAMETROS EN RUTA PARA QUE FUNCIONE */
const { id } = useParams();
const [product, setProduct] = useState({});
const productFilter = productos.find((product) => {
return product.id == id;
});
useEffect(() => {
getProduct()
.then( (prod) => {
console.log("Respuesta getProduct: ", prod)
setProduct(prod)
})
}, [id]);
const getProduct = async() => {
const docRef = doc(db, "productos", id)
const docSnapshot = await getDoc(docRef)
let product = docSnapshot.data()
product.id = docSnapshot.id
return product
}
return (
<div className="details-container">
<CardDetail data={product} />
</div>
);
};
export default CardDetailContainer;
Screenshot of console:
I would really appreciate some help, if you need more info please let me know.
Thanks

I get back an empty array from firebase 9 getDocs request

I encountered a problem in my chat app.
I works when I post message doc to the messages col but then I'm trying do getDocs back and render them I get an empty array.
I looked through FB docs, and I didn't notice any mistakes on my part. I also read an article where I was advised to use the react-firebase library with useCollectionData with which I had the same result.
const [messages, loading] = useCollectionData(
firestore.collection('messages').orderBy('createdAt')
)
I tried different approaches but nothing seems to work.
import React, { useState, useEffect } from 'react'
import { auth, db, app } from '../../firebase.config'
import { useAuthState } from 'react-firebase-hooks/auth'
import { useCollectionData } from 'react-firebase-hooks/firestore'
import { docs, onSnapshot, query, where, addDoc, collection, serverTimestamp, orderBy, getDocs } from 'firebase/firestore'
import Message from '../Message/Message'
import Spinner from '../Spinner/Spinner'
import './chat.css'
const Chat = () => {
const [user, loading, error] = useAuthState(auth)
const [value, setValue] = useState('')
const [msgs, setMsgs] = useState([])
console.log('msgs>>>>', msgs)
useEffect(() => {
const fetchMsg = async () => {
const messagesRef = collection(db, 'messages')
const q = query(
messagesRef,
orderBy('timestamp', 'desc')
)
const querySnap = await getDocs(q)
let listings = []
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data(),
})
})
setMsgs(listings)
}
fetchMsg()
}, [])
const sendMessage = async (e) => {
e.preventDefault();
const docRef = await addDoc(collection(db, 'messages'), {
uid: user.uid,
displayName: user.displayName,
photoURL: user.photoURL,
text: value,
createdAt: serverTimestamp()
})
console.log(docRef)
setValue('')
}
if (loading) {
return <Spinner />
}
return (
<>
<div className='ch-wind'>
{msgs.map((msg) => (
<Message key={msg.id} msg={msg} style={{ backgroundColor: user.uid === msg.uid ? '#A32cc4' : '#a1045a' }} />
))}
</div>
<form className="ch-form" onSubmit={sendMessage}>
<textarea
value={value}
className='ch-form-text'
onChange={e => setValue(e.target.value)}
placeholder='Enter your message here'
/>
<button
className='ch-form-btn'
>
Send
</button>
</form>
</>
)
}
export default Chat
By using useEffect() hook, I would assume that you want to get the data realtime. Firestore has a realtime listeners that you can use. You can listen to a document with the onSnapshot() method. An initial call using the callback you provide creates a document snapshot immediately with the current contents of the single document. Then, each time the contents change, another call updates the document snapshot. See code below:
useEffect(() => {
const messagesRef = query(collection(db, 'messages'), orderBy('timestamp', 'desc'));
onSnapshot(messagesRef, (snapshot) => {
// Maps the documents and sets them to the `msgs` state.
setMsgs(snapshot.docs.map(doc => ({
id: doc.id,
data: doc.data()
})))
})
}, [])
Also, as pointed out by #CDoe, you should use the same Fieldname which you set from the addDoc method as you can see on the above code.
Then on the rendering, something like this:
{msgs.map((msg) => (
// By setting the `doc.data()` to the object `data`, you should access it by `msg.data.<object_key>`
<Message key={msg.id} msg={msg.data.text} style={{ backgroundColor: user.uid === msg.data.uid ? '#A32cc4' : '#a1045a' }} />
))}
I leave some comments on the code to better understand it.
For more information on realtime updates, you may check out this documentation.
In the query, you're trying to orderBy timestamp. That's not a field you're creating in sendMessage.
When a value you're ordering by doesn't exist on the document, it won't return.
Maybe you meant to orderyBy the createdAt value.
const q = query(
messagesRef,
orderBy('createdAt', 'desc')
)

How to call api only on mount and click of Fetch Next User button

I have the below code
import React, {useState, useEffect, useCallback} from 'react';
import axios from 'axios';
const Users = () => {
const [users, setUsers] = useState([]);
const [nextPageNumber, setNextPageNumber] = useState(1);
const fetchUsers = useCallback(() => {
axios.get(`https://randomuser.me/api?page=${nextPageNumber}`).then(response => {
const updatedUsers = [...users, ...response.data.results];
setUsers(updatedUsers);
setNextPageNumber(response.data.info.page + 1);
}).catch(error => {
console.log(error)
})
}, [nextPageNumber, users])
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
const fetchNextUser = () => {
fetchUsers();
}
if(users.length === 0){
return <div>No Users available</div>
}
return(
<div>
<button onClick={fetchNextUser}>Fetch Next user</button>
{users.map(user => (
<div key={user.id}>
<div>Name: {user.name.title} {user.name.first} {user.name.last}</div>
<div>
<img src={user.picture.large} alt="Not available"/>
</div>
</div>
))}
</div>
)
}
export default Users;
This is calling the api repeatadlly. I have used the fetchUsers dependency in useEffect and useCallback hook due to eslint errors. I just want to call the api on first mount and on click of Fetch Next user button without any eslint error.
Is there any way we can achieve that?
Have a try with the below changes it will not give you the eslint error messages.
import React, {useState, useEffect, useRef, useCallback} from 'react';
import axios from 'axios';
const Users = () => {
const [users, setUsers] = useState([]);
const nextPageRef = useRef(1)
const fetchUsers = useCallback(() => {
axios.get(`https://randomuser.me/api?page=${nextPageRef.current}`).then(response => {
const updatedUsers = [...response.data.results];
setUsers(prevUsers => [...prevUsers, ...updatedUsers]);
nextPageRef.current = response.data.info.page + 1
}).catch(error => {
console.log(error)
})
}, [])
useEffect(() => {
fetchUsers();
}, [fetchUsers]);
You could simply use
useEffect(() => {
fetchUsers();
}, []);
This will call the fetchUsers function only once.
And when button is pressed, again call the fetchUsers() function

React and Firebase connection for get data and post data

I want to put the data in Firebase between li tags. For example; Room Code: {roomCode} but I cannot access the data I received from Firebase as I want.
This is the only way I can see the data in the console:
import React, {useState, useEffect, Component} from "react";
import fire from './fire';
function DataConnection() {
const [rooms, setRooms] = useState();
useEffect(() => {
const db = fire.firestore();
return db.collection('rooms').onSnapshot((snapshot) => {
const postData = [];
snapshot.forEach((doc) => postData.push({...doc.data(), id:doc.id}));
setRooms(postData);
});
}, []);
console.log(rooms);
return(
<div>
</div>
);};
export default DataConnection;
Theres nowhere in the code you are mapping the results of rooms into you return statement
function DataConnection() {
const [rooms, setRooms] = useState();
useEffect(() => {
const db = fire.firestore();
return db.collection('rooms').onSnapshot((snapshot) => {
const postData = [];
snapshot.forEach((doc) => postData.push({...doc.data(), id:doc.id}));
setRooms(postData);
});
}, []);
console.log(rooms);
rooms.map((el) => {
return (<><li>Roomname: { el.roomName} </li><li>Roomcode: {el.roomCode}</li></>)
}

React Native Undefined is not an object (evaluating 'userData.id)

I'm new to react native, I have a personal project, I am trying to get data from Firestore cloud, but I keep getting this error on the screen change.
It works fine when I comment out the database code, so I'm wondering what could be the cause.
My code
import React from "react";
import auth from "#react-native-firebase/auth";
import firestore from "#react-native-firebase/firestore";
const ProfileStackScreen = ({ navigation }) => {
React.useEffect(() => {
const usr = auth().currentUser;
setuserData(prev => {
return { ...prev, uid: usr.uid };
});
}, []);
const userRef = firestore().collection("users");
const snapshot = userRef
.where("uid", "==", userData.uid)
.onSnapshot()
.then(console.log(uid))
.catch(error => {
Alert.alert(error.message);
});
const [userData, setuserData] = React.useState({
uid: ""
// other field go here
});
return (
<View>
<Text>{userData.uid}</Text>
</View>
);
};
export default ProfileStackScreen;
You can try below code
import React from 'react';
import auth from '#react-native-firebase/auth';
import firestore from '#react-native-firebase/firestore';
const ProfileStackScreen = ({ navigation }) => {
React.useEffect(() => {
const usr = auth().currentUser;
setuserData((prev)=>{
return {...prev,uid: usr.uid};
});
}, []);
React.useEffect(() => {
fetchdata()
}, [userData]);// Once userData value has been updated then only call fetchData()
const fetchdata = ()=>{
const userRef = firestore().collection('users').doc(userData.uid).get()
.then(function (doc) {
if (doc.exists) {
console.log("Document found!");
console.log(doc.data())
} else {
console.log("No such document!");
}
});
}
const [userData, setuserData] = React.useState({
uid: '',
// other field go here
});
return (
<View>
<Text>{userData.uid}</Text>
</View>
);
};
export default ProfileStackScreen;
#Maheshvirus is right. But I think you have tried to fetch data when userData.uid is not empty.
Try this way if looking for such a way.
import React from 'react';
import auth from '#react-native-firebase/auth';
import firestore from '#react-native-firebase/firestore';
const ProfileStackScreen = ({ navigation }) => {
React.useEffect(() => {
const usr = auth().currentUser;
setuserData((prev)=> {
return {...prev,uid: usr.uid};
});
}, []);
React.useEffect(() => {
if(userData.uid !== ''){
getData()
}
}, [userData]);
const getData = () => {
firestore()
.collection('users');
.where('uid', '==', userData.uid)
.onSnapshot()
.then(() => {
console.log(uid)
})
.catch((error)=> {
Alert.alert(error.message);
});
}
const [userData, setuserData] = React.useState({
uid: '',
// other field go here
});
return (
<View>
<Text>{userData.uid}</Text>
</View>
);
};
export default ProfileStackScreen;

Categories