Im trying to just use a empty array in a react/ts project like this.
const [companyChatrooms, setCompanyChatrooms]: any = useState([]);
I then use a useEffect to get it done when rendering component.
async function fetchMyChatRooms() {
const userCollection = await firestore.collection('user_in_chat')
const snapshot = await userCollection.where('user_id', '==', myIdNumber).where('chatroom_id', '==', companyChatrooms).get();
snapshot.forEach(doc => {
const roomID = doc.data().chatroom_id
setMyChatrooms([...myChatrooms, roomID])
});
}
fetchMyChatRooms()
}, [companyChatrooms, myIdNumber])
console.log(myChatrooms)```
However, my console.log shows 2 arrays with each value instead of 1 array holding both values.
How can i make sure both values are stored in same array?
[1]: https://i.stack.imgur.com/q0WPD.png <-- Check how the output looks.
I assume you have an array snapshot with more than 1 element and any iteration you are updating the state. This caused multiple re-render
I suggest you to update state after iterate entire array. Example:
const rooms = []
snapshot.forEach(doc => {
const roomID = doc.data().chatroom_id;
rooms.push(roomID);
});
setMyChatrooms(rooms)
you should set all of them in one time.
async function fetchMyChatRooms() {
const userCollection = await firestore.collection('user_in_chat')
const snapshot = await userCollection.where('user_id', '==', myIdNumber).where('chatroom_id', '==', companyChatrooms).get();
// here is the changing
const roomIDs = snapshot.map(doc => doc.data().chatroom_id);
setMyChatrooms(roomIDs )
//
fetchMyChatRooms()
}, [companyChatrooms, myIdNumber])
console.log(myChatrooms)
Related
In the following example I am updating 2 things. The first thing is an existing array in the db, and the second thing is an existing object. The array pushing is working as it should, but the object has an issue. I mean is there an equivalent for array union to object?
// Doc model current state before the incoming update.
data
{items :[]}
{lists : {}}
const addObjToClientsArr = async () => {
const docRef = doc(db, 'data', _authContext.currentUser.uid);
const payload = selected;
// Array
await updateDoc(docRef, { items: arrayUnion(payload) });
// Obeject
const payload2 = {[context.id]: [
{ a : 1 },
]}
updateDoc(docRef, {
lists: {
payload2
});
};
It sounds like you want to update a field inside a nested object, which you can do with:
const path = `lists.${context.id}.a`;
await updateDoc(docRef, {
[path]: 1
})
I know there is a similar question that exists here but it doesn't really answer the question on here. My code currently on get the last two documents in a collection.
const [recipedata, setRecipeData] = useState([]);
const fetchRecipes = async () =>{
const response = fire.firestore().collection("recipes");
const data = await response.get();
data.docs.forEach(item=>{
setRecipeData([...recipedata, item.data()])
})
}
useEffect(() => {
fetchRecipes();
}, [])
Again when I print (recipedata) it only show two objects in the array. Can anyone tell me why this might be happening?
The query is correct and should fetch all the documents in the collection. You should check the the collection again. Also modifying the code like this should be better so you are updating the recipes array only once.
const [recipedata, setRecipeData] = useState([]);
const fetchRecipes = async () =>{
const response = fire.firestore().collection("recipes");
const data = (await response.get()).docs.map(doc => doc.data());
console.log(data)
//Updating only once
setRecipeData(data)
}
useEffect(() => {
fetchRecipes();
}, [])
Please share your collection as well so we can try replicating any issue if it has something to do with Firestore.
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({});
Why split property is undefined here? I fetched the products from my product api through axios, I received json data that have some properties(name,description,...)
const [product, setProduct] = useState({});
let substrings=[];
useEffect(() => {
const getProduct = async () => {
try {
const res = await axios.get(`/products/${props.match.params.id}`);
setProduct(res.data);
} catch (error) {
console.error(error);
}
};
getProduct();
//eslint-disable-next-line
}, []);
const substrings = product.description.split(".");
This is the json that we get from products/id
{"_id":"1","name":"Mangoes","image":"https://drive.google.com/thumbnail?id=1Iq2F4fYxDi7HdX-IJcRuON-CbNuK-pxd","description":"This sweet and fresh mangoes make your day sweet","category":"Fruits","keywords":"fruits","price":120,"countInStock":0,"content":""}
whereas it works fine here
const [product, setProduct] = useState({});
const [desc,setDesc]=useState("");
useEffect(() => {
const getProduct = async () => {
try {
const res = await axios.get(`/products/${props.match.params.id}`);
setProduct(res.data);
setDesc(res.data.description);
} catch (error) {
console.error(error);
}
};
getProduct();
//eslint-disable-next-line
}, []);
const substrings = desc.split(".");
Can anyone tell us why is it so?
I think before the load product, the value of your product is null or {},
so when you use product.description the value will be undefined.
You can use:
const substrings = (product?.description || '').split(".");
I think the problem here is the way you declared the product using useState. For the second part you declare the description directly so when you split it, it might be an empty string or whatever you declared it, but not undefined.
But for the first part, you declare just the product variable, without the description property. So before fetching, when you try to split product.description, it is undefined and becomes a value just after fetching.
In order to fix it you might declared the product like this:
const [product, setProduct] = useState({ description: "" }) or just simply use ? operator like this: const substrings = product.description?.split(".");
Also there might be a problem because you first declare substrings as an empty array and then you declare it again as a const.
Background
I'm building a "Liked Stores" screen for an app I'm developing. This screen renders a flatlist which shows cards with each store a user has liked. The way I'm handling the process is that when a user likes a store, it creates a new doc in firestore which holds the id of the store and a "liked" boolean.
Problem
I've been trying to filter the totality of stores in my db and match the id's of the liked stores with the id's in the "liked" collection documents.
The data structure is:
user/uid/favorites, each doc here contains 2 fields. An id (matching to another id in the stores/ collection) and a boolean indicating that it has been liked. And then stores/'s docs contain lots of data but what matters here is a field which holds an id.
I've tried implementing the following solution, with two subscribers bringing each collection and a function which handles the filtering. But for some reason I don't quite understand I just can't get it to work.
Code is as follows:
const [stores, setStores] = useState([]);
useEffect(() => {
const subscriber = firebase.firestore()
.collection('stores/')
.onSnapshot(querySnapshot => {
const stores = [];
querySnapshot.forEach(documentSnapshot => {
stores.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setStores(stores);
});
return () => subscriber();
}, [])
const [liked, setLiked] = useState([]);
useEffect(() => {
const subscriber = firebase.firestore()
.collection('users/' + userId + '/favorites')
.onSnapshot(querySnapshot => {
const liked = [];
querySnapshot.forEach(documentSnapshot => {
liked.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
setLiked(liked);
});
return () => subscriber();
}, [])
const usefulData = [];
const handleFilter = (obj) => {
for(let i = 0; i < stores.length; i++){
if(obj.id === liked[i].storeId) {
usefulData.push(obj)
}
}
}
stores.filter(handleFilter);
And then I'm passing usefulData as the data param to the FlatList.
When printing usefulData it simply returns an empty array.
Question
Can anyone provide a solution and an explanation as to why this is not working and how to compare two arrays of objects' values properly?
Well, figured it out in the most uncomfortable and least elegant possible way so any tips or better ideas will still be fondly accepted:
const likedIds = [];
liked.forEach(o => likedIds.push(o.id));
const storeIds = [];
stores.forEach(o => storeIds.push(o.id));
const intersection = storeIds.filter(element => likedIds.includes(element));
const [data, setData] = useState([]);
const finalArray = [];
useEffect(() => {
for (let i = 0; i < intersection.length; i++){
const fetchQuery = async () => {
const storeData = await firebase.firestore()
.collection('stores/')
.doc(intersection[i])
.get()
.then(documentSnapshot => {
if (documentSnapshot.exists) {
finalArray.push(documentSnapshot.data())
}
});
setData(finalArray)
}
fetchQuery()
}
}, [])
Without changing anything else from what is posed in the question, this is what I added in order to get this working. First I get all the Id's out of each array of objects, then filter them and store them in a new array. Then we can go ahead and build a new array of objects using a handy fetchQuery async function to get all the data from the original stores collection, just that this time it only brings up the stores with ids in the intersection array.
I am perfectly aware that this is the most bodged out and disgusting possible solution, but at the very least it works. Hope someone smarter than me can get some help out of this and make my solution much better!