What is the proper way of connecting firebase with redux-sagas?
Asked Answered
P

1

7

I am building react-native app, where I use react-native-firebase and redux-saga. This is my first project using redux-saga, hence I am learning. So far I got the authentication of the user using the following:

import firebase from 'react-native-firebase';

function* loginEmlPwdSaga(action) {
  try {
    const auth = firebase.auth();
    const data = yield call(
      [auth, auth.signInAndRetrieveDataWithEmailAndPassword],
      action.email,
      action.password,
    );
    yield put(loginSuccess(data));
  } catch (error) {
    yield put(loginFail(error));
  }
}

However, now I am stuck at the point, where I want to subscribe to the collection. In react-native-firebase I would use the following:

firebase.firestore().collection('users').onSnapshot(...)

I am not sure what is the best approach to handle subscribes in redux-saga. Can you show me the pattern that I can use for the future usage of onSnapshots (testable one)?

Parang answered 3/6, 2018 at 17:11 Comment(0)
S
5

The easiest way to subscribe to a firestore collection using redux-saga is to use a channel:

function * syncUsers () {
  const ref = app.firestore().collection('users')
  const channel = eventChannel(emit => ref.onSnapshot(emit))

  try {
    while (true) {
      const data = yield take(channel)
      yield put(successAction(data))
    }
  } catch (err) {
    yield put(errorAction(err))
  }
}

This will dispatch an action (created by successAction(data)) every time the firebase pushes a change to your client.

You can also use a library like redux-saga-firebase to automate this part and simply use:

function * syncUsers () {
  yield fork(rsf.firestore.syncCollection, 'users', {
    successActionCreator: successAction,
    failureActionCreator: errorAction
  })
}

Disclaimer: I'm the author of redux-saga-firebase.

UPDATE: redux-saga-firebase is no longer maintained and is not compatible with Firebase 8.

Schonfield answered 11/6, 2018 at 13:4 Comment(11)
I have a large data and its stopping view until all the data is not set by reducer. Any solution to avoid this problem ?Polk
@Schonfield How do you initialize redux-saga-firebase when you're using react-native-firebase? I took a look at the redux-saga-firebase docs but I'm not initializing firebase in my JS code since its done in the native code.Inconsolable
@Inconsolable it looks like react-native-firebase exports a firebase instance (import firebase from 'react-native-firebase';, see rnfirebase.io/docs/v5.x.x/installation/…) so you can then create an rsf instance with something like new ReduxSagaFirebase(firebase). I've never actually done this but it sounds liek it should work. Let me know!Schonfield
Perfect, that's what I thought it was so I had already coded it! I'm testing it today and I'll let you know how it goesInconsolable
@Inconsolable have you had success with that solution? I am currently manually using the react-native-firebase like an API but using yield call(firebase.firestore().collection('users').doc('userId').get) results in a cannot read property _customUrlOrRegion of undefined at nativeModuleKey errorVulgar
@Schonfield your suggestion worked perfectly! I wrote it exactly as you suggested. import firebase from "react-native-firebase"; const RSF = new ReduxSagaFirebase(firebase); I'm trying to listen to a document in firestore named with the current user's ID in react native but I'm having trouble passing in a variable to the to the channel. Usually I do this by passing in the userId when a fetch function is called but since I'm listening, I'm not sure how do pass in the variable on startup. Do you have any suggestions? I'm working off your syncTodosSaga example in the Firestore sectionInconsolable
@DheerajY Yes, n6g7's solution is working perfectly for me. I would suggest using his redux-saga-firebase package, it's pretty easy to work with. I posted my function to listen to firestore in the comment below, was too long for this comment. You setup this saga in your index saga as a spawn().Inconsolable
@DheerajY import firebase from "react-native-firebase"; const RSF = new ReduxSagaFirebase(firebase); export function* watchSquadChannel(userData) { try { yield put(syncSquadLoading()); const channel = RSF.firestore.channel( "collectionName/documentName", "document" ); while (true) { const response = yield take(channel); const { _data: { event: { data } } } = response; yield put(syncSquadSuccess(data)); } } catch (error) { yield put(syncSquadFailure(error)); } }Inconsolable
@Inconsolable It's difficult to help in the comments, would you mind opening a new question and posting some code examples? :)Schonfield
@Schonfield good call! I posted #57338807 . Thanks in advance for your help!Inconsolable
Ironically, I'm reading this post by @Schonfield because I need to replace his redux-saga-firebase package in my project because it's no longer maintained. :(Teshatesla

© 2022 - 2024 — McMap. All rights reserved.