react native data show on console.log but not on screen - firebase

Sometimes the app working well.
but I dont know why, sometimes the data from firebase give me blank screen instead of the data.
after I reopen the app it work.
for example,
one of my pages:
useEffect( () => {
const subscriber = firestore()
.collection('Trails')
.onSnapshot(querySnapshot => { //const querySnapshot = await firebase.firestore().collection('users').get();
const trails = [];
console.log('subscribe')
if (querySnapshot)
querySnapshot.forEach(async documentSnapshot => {
trails.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
console.log("trails test", trails)
});
setTrails(trails);
setLoading(false);
});
return () => {subscriber()};
}, []);
I made useEffect to get the data from DB then show it, and same - sometimes give me blank and sometimes works well.
I want to publish the app but im not satisfying with this bug?
sorry for my English, I dont even know how to describe this problem better.
Please can anyone guide me through? maybe my useEffect not doing well?

I think you should use debugging.
React native documentation
Stackoverflow question

I think there's issue with the return in useEffect return is called when componeent unmounts. here's an example how i handle async data fetching:
...
const [data, setData] = useState([]);
const isCurrentView = useRef(true);
useEffect(() => {
if (isCurrentView.current === true) {
const asyncFetch = async () => {
const response = await fetch(...something) //here some Asynchronous Call Like(axios, fetch)
setData([...response]);
};
asyncFetch();
}
return () => {
isCurrentView.current = false;
};
}, []);
...
im not 100% sure if this is the VERY best approach, but i have seen similar code in places so i addopted this.

problem was solved:
the setTrails was under scope and it kept refreshing with empty data.
querySnapshot.forEach(async documentSnapshot => {
trails.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
setTrails(trails); // <<<~~~~ put the set in this scope.
});
setLoading(false);
});

Related

Firestore pagination and Vue

I am trying to create Infinite scrolling pagination with Vuejs and Firestore.
So far I have been able to get the code to work in the sense that it is fetching and showing the data as intended. However when the new query is constructed after scrolling down to the bottom, the original query with the same values loads again.
It seems that I am doing something wrong with the startAfter() method, which is supposed to get the next values in the firestore query.
This is my setup:
setup() {
const latestDoc = ref(null);
const getFoods = ref([]);
onMounted(() => {
runQuery(latestDoc.value);
});
const runQuery = async (doc) => {
let q = query(collection(db, "foods"), orderBy("title"), startAfter(doc), limit(5));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
getFoods.value.push({ ...doc.data(), id: doc.id });
});
};
const loadData = (InfiniteScrollCustomEvent) => {
InfiniteScrollCustomEvent.target.complete();
latestDoc.value = getFoods.value.length - 1;
runQuery(latestDoc.value);
};
return { getFoods, optionsOutline, loadData, latestDoc, runQuery };
},
Can anyone tell me what I am doing wrong?

can we run arrow function without useEffect()?

I have a function loadListings() in react native app it gets data from real time firebase database and renders it on on page in <Flatlist />
function..
let data = [];
listingRef.orderByChild("created_at").on("value", (snapshot) => {
data = [];
snapshot.forEach((listing) => {
data.push(listing.val());
});
setListings(data);
setLoading(false);
});
};
and finally
useEffect(() => {
loadListings();
}, []);
Can I use it without the effect hook?
Yes you can. Based on official documentation for useEffect hook the [] empty array at the end means what loadListings function will be executed ones when this component will be mounted.

Google Translate API and Firebase Firestore are killing each other

We're trying to write a Google Cloud Function that gets a translation from Google Translate API, and then write the results to our Firebase Firestore database. Each works alone, but together nothing works. In other words, we can get a translation from Google Translate. We can write data to Firestore. But if we try to do both, we don't get a translation back from Google Translate, and nothing is written to Firebase. We get no error messages. We've tried the code with async await and with promises. Here's the code with promises:
exports.Google_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request').onUpdate((change, context) => {
if (change.after.data().word != undefined) {
const {Translate} = require('#google-cloud/translate');
const projectId = 'myProject-cd99d';
const translate = new Translate({
projectId: projectId,
});
// The text to translate
const text = change.after.data().word;
// The target language
const target = 'en';
let translationArray = []; // clear translation array
translate.translate(text, target)
.then(results => {
translation = results[0];
translationArray.push(translation);
try {
// write translation to dictionary
admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(text).collection('Translations').doc('English').set({
'translationArray': translationArray,
'language': 'en',
'longLanguage': 'English'
})
.then(function() {
console.log("Translation written");
})
.catch(function(error) {
console.error(error);
});
} catch (error) {
console.error(error);
}
})
.catch(error => {
console.error('ERROR:', error);
});
}
});
Here's the same code with async await:
exports.Google_EStranslateEN = functions.firestore.document('Users/{userID}/Spanish/Translation_Request').onUpdate((change, context) => { // triggers when browser writes a request word to the database
if (change.after.data().word != undefined) {
async function getTranslation() {
try {
let translationArray = []; // clear translation array
const {Translate} = require('#google-cloud/translate');
const projectId = 'myProject-cd99d';
const translate = new Translate({
projectId: projectId,
});
// The text to translate
const text = change.after.data().word;
const options = {
to: 'en',
from: 'es',
format: 'text'
}
let [translations] = await translate.translate(text, options);
translations = Array.isArray(translations) ? translations : [translations]; // If translations is an array, leave it alone; if not, put it in an array
translationArray.push(translations[0]);
await admin.firestore().collection('Dictionaries').doc('Spanish').collection('Words').doc(text).collection('Translations').doc('English').set({
'translationArray': translationArray,
'language': 'en',
'longLanguage': 'English'
})
.then(function() {
console.log("Translation written");
})
.catch(function(error) {
console.error(error);
});
// };
} catch (error) {
console.error(error);
}
} // close getTranslation
getTranslation();
}
});
You're not returning a promise that's resolved when all the async work is complete. If you don't do that, Cloud Functions assumes that all your work is complete, and will clamp down on all resources, and any pending work will be shut down.
The promise returned by translate.translate().then().catch() is being ignored. Your nested call to admin.firestore()...set() has a similar problem. It's not sufficient to just call then() and catch() on every promise because then() and catch() both return yet another promise.
You're also unnecessarily mixing usage of try/catch with catch() on the promise. You don't need both strategies for error handling, just one or the other.
When you used await in your second example, you forced JavaScript to pause until the async work represented by the promise returned by set() was complete. This allowed your function to return only after all the work was finished, which is why it worked correctly.
You might be helped by watching my video series on use of promises and async/await in Cloud Functions. Proper handling of promises is crucial to creating a correctly working function.

Nuxtjs getting firestore data within asyncData

I'm trying to convert my VueJS app to NuxtJS to work with SSR. I'm stuck trying to get data loaded with asyncData. When I add my query to a 'mounted() {}' function it works fine but I can't get it to work with asyncData(){} so that I can use SSR.
Does anyone have any idea how to fix this.
My code:
<ul>
<li v-for='province in provinces' v-bind:key="province.id"> {{province.name_nl}}</li>
</ul>
asyncData () {
return { msg: 'Welcome to my new app' }
const moment = require("moment");
var date = moment(new Date()).format("YYYY-MM-DD");
let housesArray = []
let provincesArray = []
return firebase.firestore()
.collection('provinces')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
provincesArray.push(doc.data());
});
return {provinces: provincesArray}
});
},
Or is there another way I should be doing this? Keeping in mind that it does have to work with SSR.
PS: Yes this code is inside my pages folder, not the component, I know that's not allowed.
When using asyncDate() you can add the async and await keywords to wait for the response(s) before returning them.
How I solved it:
async asyncData () {
const moment = require("moment");
var date = moment(new Date()).format("YYYY-MM-DD");
let housesArray = []
let provincesArray = []
await firebase.firestore()
.collection('provinces')
.orderBy('name_nl')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
provincesArray.push(doc.data());
});
});
await firebase.firestore()
.collection("houses")
.where("valid_until", ">", date)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
housesArray.push(doc.data());
});
});
return {
provinces: provincesArray,
houses: housesArray
}
},

correct way to use firestore onSnapShot with react redux

I'm trying to figure out the correct way to use firestore.onSnapshot with react-redux.
I currently have this code in my action file, which I am calling on componentWillMount() in my component.
export const fetchCheckins = () => async (dispatch) => {
const {currentUser} = firebaseService.auth();
try {
let timestamp = (new Date());
//set timestamp for beginning of today
timestamp.setHours(0);
//get checkins today
let checkinstoday = (await firebaseService.firestore().collection(`/checkins/${currentUser.uid}/log`).where("timestamp",">=",timestamp).orderBy("timestamp","desc").get()).docs.map(doc => doc.data());
//set timestamp for beggining of week
timestamp.setDate(-(timestamp.getDay()));
//get checkins (week)
let checkinsweek = (await firebaseService.firestore().collection(`/checkins/${currentUser.uid}/log`).where("timestamp",">=",timestamp).orderBy("timestamp","desc").get()).docs.map(doc => doc.data());
//set timestamp for begging of month
timestamp.setDate(0);
//get checkins (month)
let checkinsmonth = (await firebaseService.firestore().collection(`/checkins/${currentUser.uid}/log`).where("timestamp",">=",timestamp).orderBy("timestamp","desc").get()).docs.map(doc => doc.data());
dispatch({type: FETCH_CHECKINS, payload: { today: checkinstoday, week: checkinsweek, month: checkinsmonth}});
}
catch(e){
console.error(e);
}
};
this works fine, the correct data is sent to the component and display. The problem is, that if the user checks in, the checkin data should adjust, but it does not, since I am getting the data once and sending it, and the state is not re-rendering.
My question is how I should approach this? Do I use .onSnapshot() instead of .get()? Do I call .fetchCheckins() from the .checkin() action creator? How do I approach according to best practice? thank you
According to firestore's documentation if you need realtime updates you should use onSnapshot:
https://firebase.google.com/docs/firestore/query-data/listen
In your case if you use .get() - you get the update once and firestore won't notify you if any of the data changes. That's why you are not seeing the changes.
P.S. checkout redux-firestore: https://github.com/prescottprue/redux-firestore - it's nice library that can help you with your redux bindings.
You could subscribe your list like this:
function subscribeToExperiences() {
return eventChannel((emmiter: any) => {
experiencesRef.onSnapshot({ includeMetadataChanges: true }, snapshot => {
const experiences: IExperience[] = snapshot.docChanges().map(change => ({
id: change.doc.id,
title: change.doc.data().title
}));
if (snapshot.docChanges().length !== 0) {
emmiter(experiences);
}
});
return () => experiencesRef;
});
}
function* fetchExperiences(_: ExperiencesFetchRequested) {
const channel = yield call(subscribeToExperiences);
try {
while (true) {
const experiences = yield take(channel);
yield put(new ExperiencesFetchSucceeded(experiences));
}
} finally {
if (yield cancelled()) {
channel.close();
}
}
}
subscribeToExperiences uses a redux-saga eventChannel. An eventChannel receives an emmiter that generates a saga effect to be consumed with take. The eventChannel has to return a function to close the connections but afaik .onSnapshot connections don't need to be explicitly closed, that's why I return a dummy function.

Resources