I am using React Native and Firebase Firestore. Below is my code where the error persists. It should display true, as the document does exist inside of the firestore, however it keeps returning false. I tried testing it with a value that I manually created on the Firestore website and the .exists property was true. When I create a document with .set however, it returns false. Anyone have an explanation or a solution as to why this could be happening? I have referred to other StackOverflow articles but they were not helpful as this does not seem to be happening to anyone else. Let me know if more info is needed. Thanks in advance.
export default class GuestSession extends Component {
state = {
isLoading: true,
users: [],
code: 0
}
constructor(props) {
super(props);
let displayName = firebase.auth().currentUser.displayName
this.state.code = props.route.params.code
const docRef = firebase.firestore().collection('sessions').doc(this.state.code)
docRef.get().then((docSnapshot) => {
if(docSnapshot.exists) {
console.log("exists")
} else {
console.log("doesn't exist")
}
})
this.state.isLoading = false
}
}
I have figured out my issue. When dynamically creating documents on the go, you want to make sure that you are creating a single document first, instead of a document that includes a collection with another document. For example,
//dynamic document creation of 2 separate documents
firebase.firestore().collection('sessions').doc(this.state.code)
.collection('users').doc('exampleUserDocument').set({username: username})
should instead be written as
const sessionRef = firebase.firestore().collection('sessions')
sessionRef.doc(this.state.code).set({exampleField: value})
//properly sets session document
sessionRef.doc(this.state.code).collection('users').doc('exampleUserDocument').set({username: username})
//properly sets user document
This allows for the document to be properly created, with all property values of a document to be set for both a session document, and for a user document. I feel like when you do the longer version stated first, it skips over properly creating a document and setting proper values in the session collection to save some loading time for the system. This is more of a hypothesis on what happens rather than a factual statement, but that is my assumption. I hope this helps others in the future.
Related
i want to call multiple collections from firebase so i can display items from those collections to my home page, i have two collections salati and recipes and they have their own documents in there but i dont know if its possible to call the in single field.
Heres how my code that returns fine from one collection
constructor() {
super();
this.firestoreRef = firebase.firestore().collection('salati');
this.state = {
isLoading: true,
RecipeArr: [],
};
And heres how i tried to get multiple but didnt work
constructor() {
super();
this.firestoreRef = firebase.firestore().collection('salati, recipes');
or
this.firestoreRef = firebase.firestore().collection('salati', 'recipes');
or
this.firestoreRef = firebase.firestore().collection('salati' 'recipes');
or
this.firestoreRef = firebase.firestore().collection('salati'),('recipes');
None of the above works. Is there a solution to it or i need some different declarations?
A data loading operation from Firestore can only read data from a single collection, or from all collections with the same name. In your scenario you will have to load the results from both collections separately and then combine the results in your application code.
I am working through a simple logical problem, but I cannot seem to have things work smoothly. Let me share my most convincing code experiment and then I'll share some thoughts.
useEffect(() => {
firebase
.firestore()
.collection("someCollection")
.orderBy("date", "desc")
.onSnapshot(docs => {
let documents = []
if (canGetUpdatesFromFirestore.current) {
docs.forEach((doc) => {
documents.push(doc.data())
})
if(documents.length > 3) {
documents.splice(4, 0, {questionPostId: 0})
documents.splice(5, 0, {questionPostId: 1})
}
setAllQuestions(documents)
setUsers(documents)
}
})
if (searchValue.length > 2) {
canGetUpdatesFromFirestore.current = false;
functions.searchForSearchVal(searchValue, "Sexuality")
.then((result) => {
setAllQuestions(result);
})
} else {
canGetUpdatesFromFirestore.current = true
}
}, [searchValue])
function setUsers(docs){
let arrFinal = []
let copyOfAllQuestions = ""
for(let i = 0; i< docs.length; i++) {
console.log("HERE")
if (docs[i].postedBy) {
docs[i].ref.get().then(userFire => {
copyOfAllQuestions = {
...allQuestions,
...{hasPremium: userFire.data().hasPremium}
}
})
arrFinal.push(copyOfAllQuestions)
}
}
setAllQuestions(arrFinal)
}
Let me share some of my current state and what I am trying to accomplish.
I have a that display allQuestions. Each question data has a ref to its user document in firestore. For each question I need to check if that user hasPremium. How should I go about doing that the correct way?
The problem currently is that I can get the data from my Users collection through the ref, but I have to refresh my state in order for it all to show.
Could someone help me get on the right path / think correctly on this one please?
One approach that I put forward is to embrace data denormalization. That is, rather than putting references to other documents (Users) inside of the Questions document, put all the relevant user information directly into the Questions document.
This is antithetical to SQL database approaches, but that's okay because Firestore is "NoSQL". Embrace the anti-SQL-idity!!
Essentially, in your Question document you want to copy in whatever information is required in your app when working with a Question, and avoid doing "joins" by fetching other documents. You don't need to copy in all of the User document into a Question document - just the elements needed when your app is working with a Question.
For example, maybe in the question all you need is:
question: {
name: ...,
type: ...,
lastUpdated: ...,
postedBy: {
email: ...,
displayName: ...,
avatarUrl: ...,
hasPremium: true,
}
}
With data duplicated, you often need a mechanism to keep duplicate data up-to-date from its "source". So you might consider a Cloud Function trigger for onUpdate() of User documents, and when a relevant value is modified (email, displayName, avatarUrl, and/or hasPremium) then you would loop through all questions that are postedBy that user and update accordingly.
The rules-of-thumb here are:
all data needed for one screen/function in your app goes into a SINGLE document
NoSQL document stores are used where reads are frequent and writes are infrequent
NoSQL data stores (typically) do not have "joins" - so don't design your app to require them (which is what your code above is doing: joining Question and Users)
often you don't care about updating ALL instances of duplicated data (e.g. if a user updates their displayName today, should you update a Question they posted 3 years ago? -- different apps/business needs will give different answers)
I've got an app using mobx-state-tree that currently has a few simple stores:
Article represents an article, either sourced through a 3rd party API or written in-house
ArticleStore holds references to articles: { articles: {}, isLoading: bool }
Simple scenario
This setup works well for simple use-cases, such as fetching articles based on ID. E.g.
User navigates to /article/{articleUri}
articleStoreInstance.fetch([articleUri]) returns the article in question
The ID is picked up in render function, and is rendered using articleStoreInstance.articles.get(articleUri)
Complex scenario
For a more complex scenario, if I wanted to fetch a set of articles based on a complex query, e.g. { offset: 100, limit: 100, freeTextQuery: 'Trump' }, should I then:
Have a global SearchResult store that simply links to the articles that the user has searched for
Instantiate a one-time SearchResult store that I pass around for as long as I need it?
Keep queries and general UI state out of stores altogether?
I should add that I'd like to keep articles in the stores between page-loads to avoid re-fetching the same content over and over.
Is there a somewhat standardized way of addressing this problem? Any examples to look at?
What you need might be a Search store which keeps track of following information:
Query params (offset, limit, etc.)
Query results (results of the last search)
(Optional) Query state (isLoading)
Then to avoid storing articles in 2 places, the query results should not use Article model, but reference to Article model. Anytime you query, the actual result will be saved in existing store ArticleStore, and Search only holds references:
import { types, getParent, flow } from 'mobx-state-tree'
const Search = types.model({
params: // your own params info
results: types.array(types.reference(Article))
}).views(self => ({
get parent() {
return getParent(self) // get root node to visit ArticleStore
}
})).actions(self => ({
search: flow(function*(params) {
this.params = params // save query params
const result = yield searchByQuery(query) // your query here
this.parent.articleStore.saveArticles(result) // save result to ArticleStore
this.results = getArticleIds(result) // extract ids here for references
})
}))
Hope it's what you are looking for.
I have a collection named People that holds multiple documents. I am using Meteor JS for my website. When I run People.find({status: "Pending"}) it initially returns an empty array. If I wrap it in a Tracker.autorun:
Tracker.autorun(function(){
var peeps = People.find({status: "Pending"})
Session.set('listo', peeps)
console.log(peeps)
})
It finds the documents after first returning an empty array.
// => []
// => [{}]
The issue is that the first value (the empty array) is getting passed to my template and it is displaying none of the documents. I am using react for rendering:
render() {
return (
{
Session.get('listo').map((item) => {
HTML HERE
})
)
}
I thought that a session variable would work for this case but maybe this is conflicting with react or something, I'm not really sure.
To clarify the problem, the data always eventually gets loaded into the the listo session variable but this new data is not displayed in the render() function.
I am trying to combine some of my flat state settings into a settings state object, and at the same time, I want to convert this object to a immutable JS state object.
I get errors though that my key is not defined, although I have set the initial state in the constructor.
Here is what I have so far:
constructor() {
super();
this.state = {
"settings": Immutable.Map()
};
}
Then in componentWillMount (since I get the data from an external API):
componentWillMount() {
/* Some code */
this.setState({
settings: settings.setIn({airlines: Immutable.fromJS(airlines).toList()}),
});
}
The error I get is: Uncaught ReferenceError: settings is not defined
Btw. the airlines element is an array of objects
Can someone help me out? Is my approach directionally right? And do I need to use updateIn afterwards (on update) once the airlines array is first set and I need to update, or is it safer to use setIn?
As the error says, settings is not defined at this position.
Instead, refer to the settings slice of your state:
this.setState({
settings: this.state.settings.setIn({airlines: Immutable.fromJS(airlines).toList()}),
});
Edit:
You are also using ImmutableJS' setIn function incorrectly:
this.setState({
settings: this.state.settings.setIn(['airlines'], Immutable.fromJS(airlines).toList()),
});
See the docs or this SO answer for more details.
settings needs to be referenced like this.state.settings.setIn...