Adding items without overwriting Document in Firebase/firestore - javascript

I am trying to make an user Inventory using React js and firestore.
My document has a field for "items" which has an array. I want to add items into the array through updateDoc() but I don't know how to add the data without overwriting(erasing) the already existing items in the array.
I wish to add more properties to the items, which would make my structure look like:
User>Username>items>item>value
Or maybe I should use collections for the items? I plan to create a User for each new player and they will have a bunch of items and a bunch of properties. So I would appreciate all kinds of performance suggestion.

There's a method in Firestore library (doc for v8) called firebase.firestore.FieldValue.arrayUnion() where you can add more items to an array. It works with set() and update().
Here you have some examples in the official docs.
Another simpler method if you wish to start easier, you could retrieve the document, handle the push of the new object in your javascript/React app, and set the document again. To try at first it's fine, but I'll definitely recommend the first solution if you want to stick with arrays.

Related

Object vs Array: Is there a clear winner when storing collections of objects in state?

In a state-managing javascript framework (eg: React), if you have a collection of objects to store in state, which is the more useful and/or performant dataset type to hold them all, an object or an array? Here are a few of the differences I can think of that might come up in using them in state:
Referencing entries:
With objects you can reference an entry directly by its key, whereas with an array you would have to use a function like dataset.find(). The performance difference might be negligible when doing a single lookup on a small dataset, but I imagine it gets larger if the find function has to pore over a large set, or if you need to reference many entries at once.
Updating dataset:
With objects you can add new entries with {...dataset, [newId]: newEntry}, edit old entries with {...dataset, [id]: alteredEntry} and even edit multiple entries in one swoop with {...dataset, [id1]: alteredEntry1, [id2]: alteredEntry2}. Whereas with arrays, adding is easy [...dataset, newEntry1, newEntry2], but to edit you have to use find(), and then probably write a few lines of code cloning the dataset and/or the entry for immutability's sake. And then for editing multiple entries it's going to either require a loop of find() functions (which sounds bad for large lists) or use filter() and then have to deal with adding them back into the dataset afterwards.
Deleting
To delete a single entry from the object dataset you would do delete dataset[id] and for multiple entries you would either use a loop, or a lodash function like _.omit(). To remove entries from an array (and keep it dense) you'd have to either use findIndex() and then .slice(index, 1), or just use filter() which would work nicely for single or multiple deletes. I'm not sure about the performance implications of any of these options.
Looping/Rendering: For an array you can use dataset.map() or even easily render a specialized set on the fly with dataset.filter() or dataset.sort(). For the object to render in React you would have to use Object.values(dataset) before running one of the other iteration functions on it, which I suppose might create a performance hit depending on dataset size.
Are there any points I'm missing here? Does the usability of either one depend perhaps on how large the dataset is, or possibly how frequent the need to use "look up" operations are? Just trying to pin down what circumstances might dictate the superiority of one or the other.
There's no one real answer, the only valid answer is It dependsTM.
Though there are different use-cases that requires different solutions. It all boils down to how the data is going to be used.
A single array of objects
Best used when the order matters and when it's likely rendered as a whole list, where each item is passed from the list looping directly and where items are rarely accessed individually.
This is the quickest (least developer-time consuming) way of storing received data, if the data is already using this structure to begin with, which is often the case.
Pros of array state
Items order can be tracked easily,
Easy looping, where the individual items are passed down from the list.
It's often the original structure returned from API endpoints,
Cons of an array state
Updating an item would trigger a render of the full list.
Needs a little more code to find/edit individual items.
A single object by id
Best used when the order doesn't matter, and it's mostly used to render individual items, like on an edit item page. It's a step in the direction of a normalized state, explained in the next section.
Pros of an object state
Quick and easy to access/update by id
Cons of an object state
Can't re-order items easily
Looping requires an extra step (e.g. Object.keys().map)
Updating an item would trigger a render of the full list,
Likely needs to be parsed into the target state object structure
Normalized state
Implemented using both an object of all items by id, and an array of all the id strings.
{
items: {
byId: { /**/ },
allIds: ['abc123', 'zxy456', /* etc. */],
}
}
This becomes necessary when:
all use-cases are equally likely,
performance is a concern (e.g. huge list),
The data is nested a lot and/or duplicated at different levels,
re-rendering the list as undesirable side-effects
An example of an undesirable side-effect: updating an item, which triggers a full list re-render, could lose a modal open/close state.
Pros
Items order can be tracked,
Referencing individual items is quick,
Updating an item:
Requires minimal code
Doesn't trigger a full list render since the full list loops over allIds strings,
Changing the order is quick and clear, with minimal rendering,
Adding an item is simple but requires adding it in both dataset
Avoids duplicated objects in nested data structures
Cons
Individual removal is the worse case scenario, while not a huge deal either.
A little more code needed to manage the state overall.
Might be confusing to keep both state dataset in sync.
This approach is a common normalization process used in a lot of places, here's additional references:
Redux's state normalization is a strongly recommended best practice,
The normalizr lib.

Simple task list ordering - how to save it to Firebase Firestore?

I'm creating a simple task list web app using firebase firestore and Vue.js. All seem ok until I tried to do the task reordering feature, which turned the project into a nightmare.
I can easily implement reorder functionality on the app side by using the Vue Draggable library, that is based on the famous sortable.js library. Basically, I have a v-for code that iterates through my tasks, something like this:
<draggable v-model="tasks">
<div v-for="task in tasks" :key="task.id">{{task.title}}</div>
</draggable>
Note that this is wrapped with a draggable component that reorders the array whenever I drag elements, so that my tasks model array is dynamically reordered automatically.
All good so far, but now I'm trying to sync this reordering with Firebase Firestore, but, even though it allows me to push or remove elements into the storage array (https://firebase.google.com/docs/reference/js/firebase.firestore.FieldValue#static-arrayunion), it does not allow me to insert at specific indexes, so I can't save the reordering.
How should I approach this?
If you want to modify Firestore list field elements by index in a way that's not supported by arrayUnion or arrayRemove, you will have to:
Read the document
Modify the the array field in memory
Update the field back to the document with the new array contents

What methods or patterns can be used for rendering UI components based on supplied array?

I'm currently playing about with node.js and am looking to find a neater method for rendering a list based on an array returned to the client through an event.
In my sample application, the node server emits a 'details-changed' event which passes a simple array. At the UI end, I consume that event and render a list item for each of the array elements.
At present, I am deleting all list items and recreating them all based on the returned array. However, I would like to know if there is a more efficient method or pattern where existing items remain and only new items created and missing items removed.
Use ReactJS for handling UI updates based on the data changes from server side. It would be the best approach for manipulating the DOM updates efficiently and neatly.

Iterate through a SC.ManyArrray to create custom views to append to another view in SproutCore

I'm trying to figure out how I can retrieve a set of objects from the datastore, pass the SC.ManyArray to a controller then have a view iterate over the arrangedObjects to create custom SC.Views that I'd like to be able to append to other various views already appended to the DOM.
All I can find are examples of how to do this using the SproutCore pre built ListView/CollectionView/TableView etc. But I need these generated custom views to be appended to other views.
On another related but separate issue, when I inspect the object returned from the call to MyApp.store.find() the dataHashes property doesn't include all of my fixtures that I've included. I'm not sure if it's because I haven't created a view to call the data yet though because if I use the console I can return all the fixtures.
I'll also add that the data I'm retrieving is coming from a toMany relation from the master record. I'm retrieving the SC.ManyArray using a binding to the content of the SC.ArrayController with the path MyApp.masterModel.relationProperty.
Edit: I meant to say MyApp.masterModelController.relationProperty
Any insight would be great. I've been trying to figure this out for 2 days now and there doesn't seem to be any information found with google or the SproutCore docs.
I've figured it out...
What I ended up doing is creating a SC.CollectionView and bound the content to the SC.ArrayController used to managed the data collection. When I added this collection view to the childViews array, it automagically fetched the fixtures related to the models toMany relation and created the custom views I created and set as the exampleView on the SC.CollectionView and the stores dataHashes property was updated with these objects.
On the custom view I created to display the objects I overrode the didAppendToDocument method, acquired the path to the container view for the views I wanted to append these to and used appendChild to append the created custom views.
The last thing needed was to hide the SC.CollectionView so I just set the layout property to {top:-1000,left:-1000,width:0,height:0}.
I'm not 100% sure this is the best way to approach this but it does work (for me at least).
I'd love to hear any other suggestions to accomplish this.

Can you make a dojo.data store from properties of another dojo.data store?

Is it possible to make another dojo.data.ItemFileWriteStore out of the properties of items from an existing dojo.data.ItemFileWriteStore?
For example, i have a store containing an item with an array as a property. I want to make a new store out of the properties of one of those arrays, like so:
... new dojo.data.ItemFileWriteStore({ data:{items:selectedItem.moreItems} });
Im currently trying to do this, but dojo's magic is getting in the way. Namely, there are __id and __default properties being added to the item i fetch from the first store, and these properties cause the data for the second store to be malformed... how do i get around this?
Figured it out:
the dojo.data api sticks lots of recursive references to objects persisted inside its stores.
When you want to use parts of these objects in different stores you have to thoroughly scrub away these references. To solve this problem, i recursively took a shallow copy of all the properties of the object in question, excluding any private properties (props begining with '_'). Worked like a charm.

Categories