Delete an object inside of array with firestore - reactjs

I want to query the specific object by the property "id" inside the array and remove/delete the whole object. Is this possible through firestore?
My firestore structure looks like this:
For now I have function called removeCard() that takes in the docId which is the document id in firestore, and the cardId which is property "id" value inside the object.
removeCard() function:
export const removeCard = (retroId, cardId) => {
return (dispatch, getState, { getFirestore }) => {
// make async call to database
const firestore = getFirestore();
firestore.collection('retros').doc(retroId).update({
// firestore query to remove/delete this card.
}).then(() => {
dispatch({ type: 'DELETE_CARD_SUCCESS' });
}).catch(err => {
dispatch({ type: 'DELETE_CARD_ERROR' }, err);
});
}
};
But I cant figure out how to do this query or if it's even possible to do with Firestore. Any help is appreciated. Just tell me if i should provide with any more information to make my question more clear.

Actually, at the time of writing, it is not possible, with array-contains, to query for a specific property of an object stored in an array.
A possible workaround exists, which is to query for the entire object.
In your case it would be like
db.collection('retros').where("cardsAction", "array-contains", {id: "65....", content: "this is...", time: ....})
but obviously this workaround will not be useful in your case as both the time and content values may vary.
So in other words, you will probably need to change your data model.
One possible approach would be to duplicate your data (quite common in NoSQL world) and have another array with only the ids of the cardAction, like:
cardActionIds = ["65....", "5789", ....]
This way you can use array-contains and once you have found the docs corresponding to the cardActionIds you manipulate the other array (i.e. cardAction)

Related

Conditionally building firestore query with typescript

I'm trying to build a firestore query conditionally but I'm getting a typescript error and I don't know what I'm missing. Below is my code.
const getInitialData = ({ collection, orderClause }: ICollection) => {
let query = app.collection(collection);
if (orderClause) {
query = query.orderBy(orderClause.value, orderClause.direction);
}
query.get().then((response) => {
const genericData: T[] = [];
response.forEach((doc) => {
genericData.push(dataResolver(doc));
});
setData(genericData);
});
};
I'm getting the error "Type 'Query' is missing the following properties from type 'CollectionReference': id, parent, path, doc, addts(2739)" when updating the query inside the if statement.
Thanks in advance!
app.collection returns a CollectionReference object. query.orderBy returns a Query object. You can't assign a Query object to a CollectionReference. However, Query is a subclass of CollectionReference as you can see in the API documentation. What you can do here is make sure that your query variable is typed as a Query instead of the inferred CollectionReference so that it can hold references for either type without casting.
let query: Query = app.collection(collection);

How to change object in state without making a copy

I have a products state object that is just for front-end ease, and I want to decrement a field, but when I do, it creates a copy of the object rather than mutate the actual object. What is the React-y way to manipulate the fields without duplicates?
My download function:
const getDownload = async (id, uid, bucket_prefix, variation) => {
const getPresignedUrl = functions.httpsCallable('getPresignedUrl')
await getPresignedUrl({id: id, uid: uid, bucket_prefix: bucket_prefix ? bucket_prefix : '', variation: variation})
.then(res => {
if (Object.keys(res.data).length > 0) window.open(res.data, '_blank')
const intendedObj = ownedProds[id]
setOwnedProds({
...ownedProds,
})
})
.catch(err => console.error(err))
}
The shape of ownedProds:
ownedProds = {
{id}:
{
uid:
id:
downloads_remaining:
...
}
}
}
I basically want to target ownedProds[id] in my setState, and just make the ownedProds[id].downloads_remaining to be one less. I can do it, but it just adds another object with the same id as a key in the object.
I can move getData outside of the useEffect and just call it again, but then it's pinging my server again and getting all data all over again, and the order gets messed up so there's a shift, and it's a PITA to sort objects just because I want to mutate one property of one object in the state object. Am I being silly? Should I just call the server again for a superfluous number?
you can achieve that by passing a function to setState. The function will receive the previous value, and return an updated value. So according to what you want to do something like this should work:
setOwnedProds(ownedProds => {
ownedProds[id].downloads_remaining--;
// Or any other state manipulation you want to achieve.
return ownedProds;
});

Firestore data not rendered after calling .get() on a reference data type

I tried to get my data from a Firestore database, there's a field with 'reference' data type, here's my data structure
guns collection
{
"id":"...some id...",
"name":"M4A1",
"manufacturer":"Colt",
"gun_type:"/gun_type/...some id..." (reference)
}
gun_type collection
{
"id":"...some id...",
"name":"Assault Rifle",
"abbr":"AR"
}
I want to make the reference work like foreign key in SQL databases
Here's my code to fetch data from firestore
this.db.collection("gun").get().then((response)=>{
let newRecords = []
response.forEach((doc) => {
let data = doc.data()
// possible cause
if (data.gun_type) {
data.gun_type.get().then((res)=>{
let gunType = res.data()
newRecords.push({
id:doc.id,
gunType:gunType,
name:data.name,
manufacturer:data.manufacturer
})
})
} else {
newRecords.push({
id:doc.id,
name:data.name,
manufacturer:data.manufacturer
})
}
})
this.setState({
records:newRecords
})
}).catch((error)=>{
console.error(error)
})
Then I tried to show the records in a basic HTML table with map(), when I log the data before return statement in render(), the data is exists in this.state.records, but not showing in the table. There's no error logged in the console, When I remove codes related to gun_type reference, the data showing normally. Am I doing it wrong with reference data key? any answer will be appreciated.
sorry for my bad English
--Edit--
I noticed get() method for gun_type reference is also returns a promise, so I can't push objects with gun_type field to newRecords array. My current solution is to setState each objects directly to records state. I know it's not the best solution, but it's works. Any better solution?
When you get() a collection or a query, it returns a group snapshot of multiple documents where each document is stored inside a docs property of which you can iterate through as needed.
Simply adding .docs before your forEach can iterate through each document.
response.docs.forEach((doc) => {

How to get an object from firebase in react native

I'm trying to read object data from firebase, but since I use the code below, some error occurred.
This is my method to read data from firebase.
function getMenuData() {
let newMenu = [];
MENUref.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
const menu = {
id: doc.id,
Name: doc.data().name,
desc: doc.data().desc,
url: Images[doc.data().name],
people: doc.data().people,
type: doc.data().type,
time: doc.data().time,
ingre: doc.data().ingre,
season: doc.data().season,
like: doc.data().like,
}
newMenu.push(menu)
});
setMenu(newMenu)
setDisplayOfData(newMenu)
// console.log('food', Menu)
});
}
Then pop out "Objects are not valid as a React child." error message.
After I do some research shows it need to done through map() method, but can it performed in the "menu: {}" ?
The goal is to read the object data from firebase and make it like this.
data structure
This is my firebase data structure.
Or some available approaches can do the same thing with the object data like this?
Thanks a lot!
In the JavaScript SDK there is something known as useCollectionData than can be imported from react-firebase-hooks/firestore then you would use something such as const [data] = useCollectionData(query); where query could be a reference to the firebase collection you are pointing to with filtering capabilities ex. firestore.collection('your-collection').orderBy('createdAt). This is the official method of handling firebase data which you can then simply use with the .map(item => <Item data={item}>)
Check how your menu is being rendered to the View.
Since the menu is an array of data from firebase, to render it to the view, you have to use the map() js method because objects or array cannot be converted to a React Child element.
e.g;
{menu.map((item) => {
return <Text>{item.Name}</Text>
})}

Trying to render or merge multiple Object.keys

I am currently receiving data from Spotify via Search API.
But, I don't know how to handle multiple objects.
Screenshot
I am currently, able to: response.data.albums.items
and then map((item) => { });
Is there a way to render all of them, Albums, Artists, and Tracks?
Render to an <ul><li></li></ul>.
You can use Object.keys.
const keyArray = Object.keys(response.data);
const mergedArray = keyArray.map((key) => {
console.log(response.data[key]); // Albums, Artists, Tracks
return response.data[key].items; // I am assuming Albums,Artists and Tracks have items key.
});
console.log(mergedArray);

Resources