Using firestore data in state (React) - reactjs

How do I go about setting a specific firestore document field to state.When ever i try to run this i get the following warning.
(Im trying to get an array from firestore if that helps)
Possible Unhndled Promis Rejection (id: 0): TypeError: doc.data is not a function. (In 'doc.data()', 'doc.data' is undefined)
class Chart extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
db.collection("users")
.where("email", "==", firebase.auth().currentUser.email)
.get()
.then(doc => {
this.setState({ data: doc.data().prevlevel });
});
}
render(){
const data = this.state.data
.....

You're firing a query against a collection. A query can have multiple results, and thus returns a QuerySnapshot and not a document. Even when there is only one document matching your criteria, it'll still be in a QuerySnapshot.
So your callback will have to iterate over the results. If you only expect one result, you could do that with:
db.collection("users")
.where("email", "==", firebase.auth().currentUser.email)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
this.setState({ data: doc.data().prevlevel });
})
});

You are currently getting a collection, not a document. If you want to get a doc, you'll need to add that to your request, as in:
db.collection("users")
.where("email", "==", firebase.auth().currentUser.email)
.doc('someId') // <-- Added this, but i don't know exactly what id you need
.then(doc => {
this.setState({ data: doc.data().prevlevel });
});
Or if you do want a collection, then you'll need to use the properties found on collections, as in:
.then(snapshot => {
this.setState({ data: snapshot.docs });
})

Related

Fetch field inside a collection in firestore

I wanted to fetch the fields inside the subcollection but it is returning "undefined" and the database is designed like this:
Collection
Document
Collection
Document
Field
here is the code:
const getEventInfo = async () => {
await firebase
.firestore()
.collection("events/" + eventId + "/agenda")
.doc(customerCollectionIds)
.get()
.then((doc) => {
console.log(doc.data())
})
.catch((error) => {
console.log("Awit Error getting document:", error)
})
}
I am using reactjs. what am I missing here?

How to insert array from another query as parameter in DB call FIREBASE

I'm trying to build compound query in Expo react native - firestore.
I have 2 collections in firebase. First "node" is userID and second are IDs of places that had been discovered by this user. Then, I need to take this array of place IDs and pass it as parameter in 2nd query where I got name of each place stored in collection named "databaseOfPlaces". (I want to make scrollable view with names, so maybe I should add listener later on?)
My solution is not working very well. Can you help me? Is this the right way, or is there another way how to save DB call?
Thank you very much.
This is my code:
async componentDidMount() {
db.collection("placesExploredByUsers") // default
.doc("mUJYkbcbK6OPrlNuEPzK") // default
.collection(auth.currentUser.uid)
.get()
.then((snapshot) => {
if (snapshot.empty) {
alert("No matching documents.");
return;
}
const users = [];
snapshot.forEach((doc) => {
const data = doc.data();
users.push(data);
});
this.setState({ users: users });
})
.catch((error) => alert(error));
db.collection("databaseOfPlaces")
.where('placeID','in',this.state.users)
.get()
.then((snapshot) => {
if (snapshot.empty) {
alert("No matching documents.");
return;
}
const places = [];
snapshot.forEach((doc) => {
const data = doc.data();
places.push(data);
});
this.setState({ places: places });
})
.catch((error) => alert(error));
}
Data is loaded from Firestore (and most modern cloud APIs) asynchronously. By the time your second query now runs, the results for the first query are not available yet.
Because of this, any code that needs the results from the first query, will need to be inside the then() callback of that query.
So:
async componentDidMount() {
db.collection("placesExploredByUsers") // default
.doc("mUJYkbcbK6OPrlNuEPzK") // default
.collection(auth.currentUser.uid)
.get()
.then((snapshot) => {
if (snapshot.empty) {
alert("No matching documents.");
return;
}
const users = [];
snapshot.forEach((doc) => {
const data = doc.data();
users.push(data);
});
this.setState({ users: users });
db.collection("databaseOfPlaces")
.where('placeID','in', users)
.get()
.then((snapshot) => {
if (snapshot.empty) {
alert("No matching documents.");
return;
}
const places = [];
snapshot.forEach((doc) => {
const data = doc.data();
places.push(data);
});
this.setState({ places: places });
})
})
.catch((error) => alert(error));
}

firebase subquery and also head query in next js as getinitialprops

I want to have the query with subquery. Lets see the first query:
static async getInitialProps(ctx) {
let product = []
const PostId = ctx.query.pid;
await firebase.firestore()
.collection('products')
.doc(PostId)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id, " => ", doc.data());
});
return product
})
.catch(error => {
console.log('error', error)
})
return {product: product[0]}
}
now I got the information from that query, but I also need subcollections. I found out how to get all subcollection like this, but then I lose the data from above query.
static async getInitialProps(ctx) {
let product = []
const PostId = ctx.query.pid;
await firebase.firestore()
.collection('products')
.doc(PostId)
.collection('offers')
.orderBy('position', 'asc')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id, " => ", doc.data());
});
return product
})
.catch(error => {
console.log('error', error)
})
return {product: product[0]}
}
So is it possible to combine this toghether and get all from collection('products').doc(postID) and all from collection('products').doc(postID).collection('offers') ? So all in one query.
No, it's not possible in one query. Firestore queries are shallow and only consider documents immediately within the collection being queried. There are no SQL-like joins between collections. If you need documents from multiple collections, you will have to perform multiple queries.
The only type of query that can get documents from multiple collections is a collection group query, and those only consider documents among all collections with the same name. You could use that to query all documents among all "offers" subcollections, but you wouldn't be able to filter by anything in a parent document.

Cannot fetch api due to array react native

I bulid an api using laravel which can run in postman (http://lkcfesnotification.000webhostapp.com/api/notifications). The problem is when i fetch using an example from this (https://www.youtube.com/watch?v=IuYo009yc8w&t=430s) where there is a array in the api then i have to setstate the array which is working well but when i try using the below code it does not render due to it is not using array in the api for example the random user api have "results" :[item], and mine one is "data":[my item]
fetchData = async () => {
const response = await fetch("https://randomuser.me/api?results=500");
const json = await response.json();
this.setState({ data: json.results });
};
if i use this will work but i want to use below code due to some homework i am doing
type Props = {};
export default class IndexScreen extends Component<Props> {
...
this.state = {
data: [],
isFetching: false,
};
_load() {
let url = "http://lkcfesnotification.000webhostapp.com/api/notifications";
this.setState({isFetching: true});
fetch(url)
.then((response) => {
if(!response.ok) {
Alert.alert('Error', response.status.toString());
throw Error('Error ' + response.status);
}
return response.json()
})
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
.catch((error) => {
console.log(error)
});
}
https://imgur.com/a/he5mNXv this is my render
the result i get the code i run is blank is loading
The fetch request is working but you are not saving the right data in the right state property.
The issues is located in the following part:
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
You are assigning the response to a variable members but saving another variable data, which does not exist.
In addition, the response is an object with more information than just the data, so what you are looking for is just the data property of the response.
This should work:
.then(({ data }) => {
this.setState({data});
this.setState({isFetching: false});
})
Here we destructure the response into the variable { data }, solving your issue.
Based on the snippets you don't use the fetched data to set it to your state:
.then((members) => {
this.setState({data});
this.setState({isFetching: false});
})
membersis the result of your fetched json. So either rename members to data or use data: members. If the code should work like your first function it's probably data: members.result. You can also combine the two setState calls to one single call:
this.setState({
data: members.result,
isFetching: false,
});

Map items of collection snapshot in Firebase Firestore

Firebase Firestore Guides show how to iterate documents in a collection snapshot with forEach:
db.collection("cities").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
});
});
I imagined it would support map as well, but it doesn't. How can I map the snapshot?
The answer is:
querySnapshot.docs.map(function(doc) {
# do something
})
The Reference page for Firestore reveals the docs property on the snapshot.
docs non-null Array of non-null firebase.firestore.DocumentSnapshot
An array of all the documents in the QuerySnapshot.
Got pretty sick and tired of Firestore returning stuff in their classes or whatever. Here's a helper that if you give it a db and collection it will return all the records in that collection as a promise that resolves an actual array.
const docsArr = (db, collection) => {
return db
.collection(collection)
.get()
.then(snapshot => snapshot.docs.map(x => x.data()))
}
;(async () => {
const arr = await docsArr(myDb, myCollection)
console.log(arr)
})()
// https://firebase.google.com/docs/firestore/query-data/get-data
const querySnapshot = await db.collection("students").get();
// https://firebase.google.com/docs/reference/js/firebase.firestore.QuerySnapshot?authuser=0#docs
querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
Here's another example
var favEventIds = ["abc", "123"];
const modifiedEvents = eventListSnapshot.docs.map(function (doc) {
const eventData = doc.data()
eventData.id = doc.id
eventData.is_favorite = favEventIds.includes(doc.id)
return eventData
})
I have found that a better way to do this by using map and get your document id as well is as follows:
start with the object array I wish to update in your constructor:
this.state = {
allmystuffData: [
{id: null,LO_Name: "name", LO_Birthday: {seconds: 0, nanoseconds: 0},
LO_Gender: "Gender", LO_Avatar: "https://someimage", LO_Type: "xxxxx"},],
};
and in my function do the following
const profile = firebase
.firestore()
.collection("users")
.doc(user.uid)
.collection("stuff")
.get()
.then( async (querySnapshot) => {
console.log("number of stuff records for ",user.uid," record count is: ",
querySnapshot.size);
const profile = await Promise.all(querySnapshot.docs.map( async (doc) => {
const stuffData = doc.data()
stuffData.id = doc.id
condole.log("doc.id => ",doc.id)
return stuffData
}));
this.setState({allmystuffData: profile});
})
.catch(function (error) {
console.log("error getting stuff: ", error);
})
In this example I read all the documents in the collection with querysnapshot the when mapping accross them. the promise.all ensures that all the records are returned before you render it to your screen. I add the document id to the "id" element of each object in the array returned, then I use setstate to replace my state array with the array returned from the query.
you can try this
FirebaseFirestore.instance
.collection('Video_Requests')
.get()
.then((QuerySnapshot querySnapshot){querySnapshot.docs.forEach((doc){
print(doc.data());
});
});

Resources