Reputation: 31
I'm making an app that fetch events object from firestore to display it on a map.
I implemented redux-saga to do the async call to firestore API on componentDidMount in order to display the results on the map.
I have 3 actions (LOAD_EVENTS_LOADING
/ LOAD_EVENTS_SUCCESS
/ LOAD_EVENTS_ERROR
) so that I can display a loading component before rendering the results.
saga.js :
> import { put, call, takeLatest } from 'redux-saga/effects' import {
> getEventsFromGeoloc } from '../../firebaseAPI/APImethods'
>
> function* fetchEvents(action) {
> try {
>
> const events = yield call(getEventsFromGeoloc, {latMarker : action.payload.latMarker, longMarker: action.payload.longMarker,
> circleRadius: action.payload.circleRadius});
> yield put({type: 'LOAD_EVENTS_SUCCESS', fetchedEvents: events});
> } catch (e) {
> yield put({type: 'LOAD_EVENTS_ERROR', error: e.message});
> } }
>
> export function* eventsSaga() {
> yield takeLatest('LOAD_EVENTS_LOADING', fetchEvents); }
>
> export default eventsSaga;
My problem is that in my saga the action "LOAD_EVENTS_SUCCESS" is dispatched before the API call ends.
How do I make sure the API call is completed before dispatching the "LOAD_EVENTS_SUCCESS" action ? Thanks for your help !
Here is my API method :
import firebase from 'react-native-firebase';
import { GeoFirestore } from 'geofirestore';
export const getEventsFromGeoloc = (payload) => {
let events =[]
const geoFirestore = new GeoFirestore(firebase.firestore());
const geoCollection = geoFirestore.collection('events');
const query = geoCollection.near({
center: new firebase.firestore.GeoPoint(payload.latMarker, payload.longMarker),
radius: payload.circleRadius
});
query.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
const idEvent = doc.id
const eventData = doc.data()
events.push({idEvent, eventData})
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
return events;
}
Upvotes: 3
Views: 5216
Reputation: 31
thanks all for your answers. I did a saga with my getEventsFromGeoloc and with a yield for the query its working like I expected : It waits for the complete query to run and then return the array of events and dispatch the LOAD_EVENTS_SUCCESS action.
Here is my saga :
import { put, takeLatest } from 'redux-saga/effects'
import firebase from 'react-native-firebase';
import { GeoFirestore } from 'geofirestore';
function* getEventsFromGeoloc(action) {
try{
let events =[]
const geoFirestore = new GeoFirestore(firebase.firestore());
const geoCollection = geoFirestore.collection('events');
const query = geoCollection.near({
center: new firebase.firestore.GeoPoint(action.payload.latMarker, action.payload.longMarker),
radius: action.payload.circleRadius
});
yield query.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
const idEvent = doc.id
const eventData = doc.data()
events.push({idEvent, eventData})
})})
yield put({type: 'LOAD_EVENTS_SUCCESS', fetchedEvents: events});
}
catch(error) {
console.log("Error getting documents: ", error);
yield put({type: 'LOAD_EVENTS_ERROR', error: error.message});
}
}
export function* eventsSaga() {
yield takeLatest('LOAD_EVENTS_LOADING', getEventsFromGeoloc);
}
export default eventsSaga;
Upvotes: 0
Reputation: 1448
Firebase works differently than you would expect. The firebase queries return immediately, they don't wait for the query to finish and be processed by your code before it returns.
What I ended up doing in my own apps, was instead of calling the success/fail action from the original saga function, I called them from within my query callback, something like this:
query.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
const idEvent = doc.id
const eventData = doc.data()
events.push({idEvent, eventData})
});
yield put({type: 'LOAD_EVENTS_SUCCESS', fetchedEvents: events});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
yield put({type: 'LOAD_EVENTS_ERROR', error: error.message});
});
NOTE: This will also require you make your getEventsFromGeoloc
function a generator function, or a saga, to use the yield
statements as well as import the put
saga effect.
OTHER NOTE: You could make the function more generic by passing in a callback function for success and failure and just call the callback instead of hard coding the action you are dispatching like this. Or you could pass in the action type and payload keys to build the action inside the completion callback for firebase.
Let me know if that wasn't helpful enough, or if you have questions about what I've put up here.
Upvotes: 0
Reputation: 1021
call(fn, ...args)
fn
: Function - A Generator function, or normal function which either returns a Promise as result, or any other value.
Refer this docs
In your case you are returning events
from your getEventsFromGeoloc
func even when your response has not yet arrived.
Upvotes: 0
Reputation: 36924
getEventsFromGeoloc
is not returning a promise for redux-saga to await, that's the problem
Just return a promise that resolve with the events:
export const getEventsFromGeoloc = (payload) => {
// ...
return query.get()
.then(function(querySnapshot) {
const events = []
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
const idEvent = doc.id
const eventData = doc.data()
events.push({idEvent, eventData})
});
return events
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
}
Upvotes: 4