correct way to use firestore onSnapShot with react redux

15,963

Solution 1

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.

Solution 2

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.

Share:
15,963
Jakob
Author by

Jakob

Updated on June 08, 2022

Comments

  • Jakob
    Jakob almost 2 years

    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

  • Rob Grzywinski
    Rob Grzywinski over 5 years
    Are you sure that the connections don't need to be closed? @FrankvanPuffelen indicates that they need to be
  • virtualLast
    virtualLast about 5 years
    Are there any good examples of how to use redux-firestore, I have firestore appearing in my state object, but I am unsure of how to get collections