React Native w/ firebase "undefined is not an object" - javascript

Getting this error:
TypeError: undefined is not an object (evaluating 'doc.data().books')
from this code:
const getData = () => {
const snapshot = db.collection('users').doc(userEmail).onSnapshot((doc) => {
setData(doc.data().books)
})
}
useEffect(() => {
getData()
}, [])
this is the firestore database, trying to get to the updated boo collection (gets shown in a flatlist that needs to get updated when new content is added to the collection) Note that there's a users collection that is offscreen.
I'm pretty sure it's something that I'm doing wrong on my snapshot but I'm really not sure what, all help is greatly appreciated :)

The error is telling you that doc.data() returned undefined, which means the document you requested doesn't exist. You probably passed a value for userEmail other than what you expected it to be. Since you didn't show the value, we don't know for sure what went wrong.
I will point out that document IDs must be exact matches, so if the email address wasn't exactly "Email#email.com" with a capital E, the query will not return a document using what you've shown here.

There are several possibilities. However, db is not defined within the scope of the getData function... where is it defined?

If I correctly understand your question, you want to get the documents in the books (or boo?) subcollection of the user document with id corresponding to userEmail.
If this assumption is correct, you should do as follows:
const getData = () => {
db.collection('users').doc(userEmail).collection('books')
.onSnapshot(querySnapshot => {
var books = [];
querySnapshot.forEach(doc => {
books.push(doc.data());
});
// Here you have an array of all the book objects.
// It's up to you to use it as you wish with your setData() function
});
}
Note that with doc.data().books you are actually trying to get the books field of the user doc.

Related

React setState, using array methods in callback function

I am currently following a React course on Scrimba on creating a web app for taking notes.
The problem requires me to bump a note to the top of the note list every time it's updated.
The notes are initialised through useState as follows:
const [notes, setNotes] = useState([])
The array consists of the individual notes as objects with an id and body
Every time an onChange is triggered, the following function is ran:
function updateNote(text) {
setNotes(oldNotes => {
let updated = oldNotes.map(oldNote => {
return oldNote.id === currentNoteId
? { ...oldNote, body: text }
: oldNote
})
const currNoteIndex = updated.findIndex(
note => note.id === currentNoteId
)
console.log(currNoteIndex)
updated.unshift(updated.splice(currNoteIndex, 1))
return updated
})
}
However, I keep getting an error as shown in the image.
It's very unclear to me where the problem lies, but I'm thinking it has to do with the array methods.
Any explanation for this issue would be greatly appreciated!
Credits to jsN00b for the answer:
array.splice returns an array, not the object.
Since that array is inserted to the start of the array containing the objects, there will be an error when updateNote() is called again.

How to enable persistence on reactfire?

I'd like to implement Firestore offline persistence on my PWA React app using the reactfire library.
const firestore = useFirestore().enablePersistence();
let documentReference = firestore
.collection("food")
.doc("milkshake");
const { data } = useFirestoreDocData(documentReference);
but running the code i get an error:
FirebaseError: Firestore has already been started and persistence can no longer be enabled. You can only enable persistence before calling any other methods on a Firestore object.
This component is wrapped inside a <Suspense> as mentioned in the documentation
That database read is the only one that i make in the entire app, how can i solve?
Edit.
Using the example that #Ajordat gave, I've imported the preloadFirestore function inside the App component I do get an error:
"Cannot read property 'name' of undefined".
Whereas adapting (because I cannot use hooks inside the fetch function)
the example from #DougStevenson: I've imported useFirestore function in the App component (in order to get the Firestore object) to enable persistence, and then importing it (useFirestore) into my component in order to retrieve the data, but now, I get the same error as before,
Firestore has already been started and persistence can no longer be enabled.
Edit 2:
I've tried to enablePersistence without errors, thank guys, this is my approach, let me know if it is the best:
const firestore = useFirestore();
React.useEffect(() => {
firestore.enablePersistence();
}, []);
And in my custom component:
let docRef = useFirestore()
.collection("food")
.doc("milkshake");
let document = useFirestoreDocDataOnce(docRef);
console.log(document)
But now I do have a problem, when I log the document, the data are not emitted instantly, yeah I know that it is an asynchronous operation, but the component is wrapped inside a <Suspense>, in this way:
<Suspense fallback={<div>Loading</div>}>
<FoodComponent foodName={"Milkshake"} />
</Suspense>
But I don't see the loading text before the component is actually rendered.
Does the suspense fragment show the fallback component only while is loading the function (useFirestore) and not the actual data?
Well, I've solved, have to destructure the data, doing like that:
let docRef = useFirestore()
.collection("food")
.doc("milkshake");
let { data: document } = useFirestoreDocData(docRef);
console.log(document)
On other JavaScript libraries for Firestore, enablePersistence() returns a promise. That means it will complete some time in the future, with no guarantees how long it will take. If you're executing the query immediately after you call enablePersistence(), without waiting for the returned promise to become fulfilled, then you will see this error message. That's because the query "beats" the persistence layer and effectively executes first.
You will have to figure out how to use that promise to wait until it's OK to make that query with persistence enabled. For example:
seFirestore().enablePersistence()
.then(() => {
let documentReference = firestore
.collection("food")
.doc("milkshake");
const { data } = useFirestoreDocData(documentReference);
})
.catch(error => {
console.error("enablePersistence failed", error);
})
Notice how the query will complete only after the persistence is fully enabled.
Thanks for the suggestion guys #DougStevenson and #Ajordat
In app component:
import { useFirestore } from "reactfire"
...
const firestore = useFirestore();
React.useEffect(() => {
firestore.enablePersistence();
}, []);
In your custom component, where you want to use Firestore:
import { useFirestore, useFirestoreDocData /* or what you want to use */ } from "reactfire"
let docRef = useFirestore()
.collection("food")
.doc("milkshake");
let { data: document } = useFirestoreDocData(docRef);
console.log(document);

How to Sort/Filter Firebase Realtime Database with Query in Javascript

I'm having a hard time understanding why this simple query is not giving me the expected results.
In my Firebase realtime database my structure looks like this:
Pretty simple, right? Next I have a cloud function that runs whenever I manually change one of the orbs values under any of the users. This is my function:
exports.testTopPlayers2 = functions.database.ref('/TestUsers/{user}/Statistics').onUpdate(_ => {
const allPlayersRef = admin.database().ref('/TestUsers');
const query = allPlayersRef.orderByChild('orbs').limitToLast(2)
return query.on('value', (snap, context) => {
return console.log(snap.val());
})
Also very straightforward I would think. In regular english, what I'm expecting that function to do is: "When any of the orbs values are changed, run this function to sort all of the users by their orbs value (lowest to highest) and take a new DataSnapshot of only the final 2 users' data (The users with the highest number of orbs)".
When I print to the console, I'm expecting to see the data from User3 & User6 because they have the highest number of orbs... but instead it seems to be sorting by the username (User5 & User6) or simply not sorting at all. Here is the log output:
Which clearly does not sort by what I'm defining in my query.
Maybe I'm missing something obvious but I've stared at it long enough... hoping someone can spot a mistake in my function regarding how it's sorting.
I appreciate the help!
You're looking for:
allPlayersRef.orderByChild('Statistics/orbs').limitToLast(2)
Note that you'll also want to use once() instead of on():
return query.once('value').then((snap) => {
return snap.val();
})
You'll also want to remove that console.log around snap.val(), since console.log() doesn't return anything.
Finally: when you call .val() on a snapshot, you're converting it to key-value pairs and lose the ordering information. If you want to maintain the ordering, you'll want to use forEach:
return query.once('value').then((snap) => {
let results = [];
snap.forEach((player) => {
results.push( {key: snap.key, ...snap.val() }
});
return results;
})
Thanks to Frank's thorough answer, I was able to understand what needs to happen and got it working perfectly with minor tweaks.
With the exact modifications suggested by Frank, my output in the Firebase console looked like this:
Which is a lot better than what I was able to produce until now, but it still wasn't showing the exact data I wanted to use.
I wasn't interested in the key of the snap (which is the top-level TestUsers node), but rather I wanted the key of the player which provides the username responsible for the high score.
Next, Instead of getting an [object] for the orb values, I needed the actual value of the orbs, so I had to dive into the children of each player object to get that value.
This is my tweaked cloud function in all it's glory lol:
exports.testTopPlayers2 = functions.database.ref('/TestUsers/{user}/Statistics').onUpdate(_ => {
const allPlayersRef = admin.database().ref('/TestUsers');
const query = allPlayersRef.orderByChild('/Statistics/orbs').limitToLast(2)
return query.once('value').then((snap) => {
let results = [];
snap.forEach((player) => {
let username = player.key
let orbsValue = player.child("Statistics").child("orbs").val()
results.push( {Player: username, Score: orbsValue } )
});
console.log(results)
return results;
})
})
And the result of the Firebase log is this:
This is exactly what I was after and can now proceed :D

Javascript saying object value is undefined. What am I doing wrong?

I'm trying to display the value of an object within an array of Github repositories (retrieved from a parsed JSON) in my React app but I'm getting an error that the browser can't read property key of undefined.
I'm able to console.log the JSON data and the objects within it but if I try to log the values within those objects, I get the error.
const App = () => {
const [repoList, setRepoList] = useState({});
useEffect(() => {
fetch("https://example.com/api")
.then(res => res.json())
.then(data => setRepoList(data));
});
return (
<div>{repoList[0].id}</div>
)
}
If I put JSON.stringify(repoList) within the div element, it shows the data.
If I put JSON.stringify(repoList[0]) within the div element, it also shows the data.
But if I put something like repoList[0].id nothing appears. This happens with each key listed. And I can confirm that the key is there. What am I doing wrong?
Try this.
const [repoList, setRepoList] = useState([]); // Change to array not object.
...
return repoList.length ?
(<React.Fragment>{repoList[0].id}</React.Fragment>)
: (<span>Loading ....</span>)
If your div does not have styles, better user Fragment, it result in better performance.
You're calling async functions to populate the repo list. The first time the return tries to use repoList[0].id, there is no repoList[0]. You may want to initialize repoList to a non-error-ing dummy object when you set it up with useState({})

Trying to destructure a value from an object returns an error

I'm trying to fetch a name property from an object using destructuring but the error returning says 'cannot read property name of undefined.
// Get genres for given movie
getGenres = (genreIds, genres) => {
return genreIds.map(genreId => {
const filteredGenres = genres.filter(genre => genre.id === genreId);
const { name } = filteredGenres[0];
return name;
})
};
If I console log filteredGenres[0] I do get a valid object back with a name and id, I've also checked this with typeof to ensure. The result is:
{id: 14, name: "Fantasy"}
If i try to console log filteredGenres[0].name I also get the same cannot read property name of undefined error.
I'm not entirely sure what I'm doing wrong? Any help is really greatly appreciated! :)
I've created a stackblitz as requested. File in question is NowPlaying.js
https://stackblitz.com/edit/react-ow3x3u
As per your Stackblitz sample, the state property genres is initialized as an empty array.
This means that when you pass this as a prop to NowPlaying, filterGenres will also be an empty array and thus the error you get.
This happens only before the genres are fetched. After genres is fetch and the state is updated, the state is updated and everything looks fine.
You have to account for the genres array being empty.

Categories

Resources