I'm unable to define a collection reference using redux-saga-firebase. I'm trying to define it like this:
const query = rsf.firestore.collection('players').where('up', '>', lastUpdated);
but I am getting
TypeError: _firebase_firebase__WEBPACK_IMPORTED_MODULE_6__.rsf.firestore.collection is not a function
I have tried also tried using .reference() as described here to no avail. I'm attempting to limit the number of documents synced from a collection per this suggestion. How can I get a collection reference for use in this function?
import { rsf } from '../../firebase/firebase';
export function* getPlayersFromDb() {
const players = yield select(getPlayers);
const lastUpdated = Math.max(...players.map(x => x.up));
const query = rsf.firestore.collection('players').where('up', '>', lastUpdated);
yield fork(
rsf.firestore.syncCollection,
query,
{ successActionCreator: response => ({
type: t.GET_PLAYERS_FROM_DB_SUCCESS,
payload: [...response],
}),
failureActionCreator: () => ({
type: t.GET_PLAYERS_FROM_DB_FAILURE,
payload: [],
}),
transform: response => messageTransformer(response),
}
);
}
The problem with your code is that you are mixing the official firestore SDK libraries with the redux-saga-firestore ones. If you want to get a collectionReference you have to do it using the official SDK, since the redux-saga-firestore libraries do not offer a method that returns that output.
So, in order to get the collectionReference you would need to import the Firestore SDK and use almost the same code you already have on the getPlayersFromDb function, and it would look like this:
const firebase = require("firebase");
require("firebase/firestore");
var db = firebase.firestore();
...
export function* getPlayersFromDb() {
...
const query = db.collection('players').where('up', '>', lastUpdated);
...
}
NOTE: As you can see on this redux-saga-firestore documentation, you could be using the getCollection method for that, but since you have a where clause, you would need to use a collectionReference to filter it anyway, so I am afraid there is no real solution for this with redux-saga-firestore.
Hope this helps
Related
I'm trying to log every possible error to a firestore document, to achieve this, I have a function which receives the error object, and saves it to the firestore document... the thing is... that this error object can be anything, it can be a single string and not an object, or it could be a complex object, appearently with functions aswell, but firestore won't accept some data types, for example if the object has an undefined property firestore will not accept it...
So I'm trying to find a way to sanitize or clean an object from which I don't know what properties could have.
Right now i'm using this:
import {db} from 'src/firebase/config'
import { addDoc, collection } from 'firebase/firestore';
import {Platform} from 'quasar'
export const saveErrorLog = async (hotel, uid, ref, error) => {
const guestRef = collection(db, 'error_logs');
const cleanErrorObject = Object.entries(error).reduce((a,[k,v]) => (v ? (a[k]=v, a) : a), {})
const logData = {
platform: Platform.is || null,
ref: ref || null,
error: { ...cleanErrorObject },
hotel: hotel || null,
uid: uid || null
}
await addDoc( guestRef, logData )
}
This works most of the time, but sometimes firebase returns the same error of unsupported data types... I think it is because some error objects have functions aswell, but I'm not sure about it.
Thanks in advance.
Without using redux-thunk, I want to add an object(expense) to firebase realtime database inside the action generator function before returning the action itself. I don't see the need to redux-thunk if I am able to execute the async function.
Here is the code in src/actions/expenses.js
export const addExpense = ({description= '', note= '', createdAt= 0, amount= 0} = {}) => {
const expense = {
id: uuidv4(),
description,
note,
createdAt,
amount
}
const db = firebase.database()
db.ref('expenses').push(expense)
return {
type: 'ADD',
expense
}
}
While it would technically work, this way you are making harder to write tests (see this example).
If you don't care about tests... you probably didn't need redux in the first place
I have a basic database that essentially stores an array of product id's underneath a user. The user can select products to add to the array so it makes sense to use 'arrayUnion' so I avoid reading and re-writing the array constantly, however, I keep getting the error *"Property 'firestore' does not exist on type 'FirebaseNamespace'."
I've followed the documentation found at: https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array but I fear I'm still using it incorrectly.
My code for updating the array is:
addToPad(notepadName: string){
const updateRef = this.db.collection('users').doc(this.activeUserID).collection('notepads').doc(notepadName);
updateRef.update({
products: firebase.firestore.FieldValue.arrayUnion(this.productId)
});
}
First you need to import firestore:
import { firestore } from 'firebase/app';
Then you will be able to use arrayUnion:
addToPad(notepadName: string){
const updateRef = this.db.collection('users').doc(this.activeUserID).collection('notepads').doc(notepadName);
updateRef.update({
products: firestore.FieldValue.arrayUnion(this.productId)
});
}
import { arrayUnion } from '#angular/fire/firestore'
const path = `ai/${videoId}/panel-operation/${id}`
const myDoc: AngularFirestoreDocument<any> = this.afs.doc<any>(path)
const promise: Promise<void> = myDoc.update({ auxPanelSelections: arrayUnion({auxPanel: 'clip', operation: 'replace'}) }).catch((err: any) => {
console.error(`oopsie - ${err.message}`)
return null
})
auxPanelSelections is an array within the myDoc document
Note that the above code also works perfectly fine with arrayRemove
I cannot find the #angular/fire docs for arrayUnion but the generic docs are here
I am very new to react and Firebase and I really struggle with arrays and objects I'm guessing that you can't use .map with the way my data is formatted (or type). I've looked through stack but nothing has helped (at least in my poor efforts to implement fixes).
I am trying to use .map to map through a result from firebase but I get the error TypeError: this.state.firebasedata.map is not a function.
getting the data:
componentWillMount(){
this.getVideosFromFirebase()
}
getVideosFromFirebase(){
var youtubeVideos = firebase.database().ref('videos/');
youtubeVideos.on('value', (snapshot) => {
const firebasedata = snapshot.val();
this.setState({firebasedata});
});
}
relevant states:
constructor(props){
super(props);
this.state = {
firebasedata: []
}
};
.map in render:
render(){
return(
<div>
{this.state.firebasedata.map((item) =>
<div key="{item}">
<p>{item.video.name}</p>
</div>
)}
</div>
);
}
This is because firebase does not store data as arrays, but instead as objects. So the response you're getting is an object.
Firebase has no native support for arrays. If you store an array, it really gets stored as an "object" with integers as the key names.
Read this for more on why firebase stores data as objects.
To map over objects you can do something like
Object.keys(myObject).map(function(key, index) {
console.log(myObject[key])
});
For anyone coming here now, you can simply type snapshot.docs to get an array of all the documents in the relevant collection.
As an example, if you want to get all user objects from the collection users you can do the following:
const getAllUsers = async () => {
const usersSnapshot = await db.collection('users').get()
const allUsers = usersSnapshot.docs.map(userDoc => userDoc.data())
return allUsers
}
As noted above Firebase snap.val() - is an object.
When you have to go through object you can also simply use for each:
for(var key in this.state.firebasedata){
<div key="{key}">
<p>{this.state.firebasedata[key].video.name}</p>
</div>
}
I also recommend creating a separate method for it, and call it in render.
Hope this helps
From the Firebase note:
Given a single key path like alanisawesome, updateChildren() only updates data at the first child level, and any data passed in beyond the first child level is a treated as a setValue() operation. Multi-path behavior allows longer paths (like alanisawesome/nickname) to be used without overwriting data. This is why the first example differs from the second example.
I am trying to use a single function createOrUpdateData(object) in my code. In case of update, it updates first level children properly, but if I have nested object passed, then it deletes all other properties of that nested object.
Here's the code:
function saveUserDetails(email,object){
var hashedEmail = Utilities.getHashCode(email);
var userRef = ref.child(hashedEmail);
return $q(function(resolve,reject){
return userRef.update(object, function(error){
if(error){
reject(error);
}else{
resolve("Updated successfully!");
}
});
});
}
So if I pass:
{
name: 'Rohan Dalvi',
externalLinks: {
website: 'mywebsite'
}
}
Then it will delete other properties inside externalLinks object. Is there a cleaner and simpler way of avoiding this?
In short, how do I make sure nested objects are only updated and that data is not deleted.
You can use multi-path updates.
var userRef = ref.child(hashedEmail);
var updateObject = {
name: 'Rohan Dalvi',
"externalLinks/website": 'mywebsite'
};
userRef.update(updateObject);
By using the "externalLinks/website" syntax in the object literal it will treat the nested path as an update and not a set for the nested object. This keeps nested data from being deleted.
This question provides a more recent solution that works with cloud firestore.
Rather than using "/", one may use "." instead:
var userRef = ref.child(hashedEmail);
var updateObject = {
name: 'Rohan Dalvi',
"externalLinks.website": 'mywebsite'
};
userRef.update(updateObject);
To update nested object/map/dictionary in firebase database, you can use Firestore.Encoder to class/struct that is Codable.
Here is a Swift code example:
Models:
import FirebaseFirestore
import FirebaseFirestoreSwift
// UserDetails Model
class UserDetailsModel: Codable {
let name: String,
externalLinks: ExternalLinkModel
}
// UserDetails Model
class ExternalLinkModel: Codable {
let website: String
}
Calling Firebase:
import FirebaseFirestore
import FirebaseFirestoreSwift
let firestoreEncoder = Firestore.Encoder()
let fields: [String: Any] = [
// using firestore encoder to convert object to firebase map
"externalLinks": try! firestoreEncoder.encode(externalLinkModel)
]
db.collection(path)
.document(userId)
.updateData(fields, completion: { error in
...
})