groverk912
groverk912

Reputation: 33

Redux saga: yield put not working inside nested callback

const { payload: {loginType, email, password, notification, self} } = action;

  console.log("--TRY--");
  Firebase.login(loginType, { email, password })
    .catch(function(result) {
      const message =
        result && result.message ? result.message : 'Sorry Some error occurs';
      notification('error', message);
      self.setState({
        confirmLoading: false
      });
      isError = true;
    })
    .then(function(result) {
      if (isError) {
        return;
      }
      if (!result || result.message) {
        const message =
          result && result.message
            ? result.message
            : 'Sorry Some error occurs';
        notification('error', message);
        self.setState({
          confirmLoading: false
        });
      } else {
        self.setState({
          visible: false,
          confirmLoading: false
        });
        console.log("--RIGHT BEFORE I CHECK AUTH STATE--");

        //the following does NOT fire
        firebaseAuth().onAuthStateChanged(function*(user) {
              console.log("THE GENERATOR RUNS");
              if (user) {
                  console.log(user);
                  yield put({
                      type: actions.LOGIN_SUCCESS,
                      token: 'secret token',
                      profile: 'Profile'
                  });
                  yield put(push('/dashboard'));
              }
              else {
                  yield put({ type: actions.LOGIN_ERROR });
              }
          });
      }
  }); });

Hi. I'm currently working with redux saga for the first time. I've been trying to get yield put to fire in the callback of the firebaseAuth().onAuthStateChanged listener. The yield keyword won't work in a function that is not an ES6 generator, so I added an asterisk to the callback but now it won't execute at all. Would really appreciate any advice on the matter.

Upvotes: 0

Views: 3871

Answers (1)

VonD
VonD

Reputation: 5155

As you noticed, redux-saga effects can only be used within a generator function, and you cannot use a generator function as a regular function: calling a generator function only returns a special object.

The right way to approach this is to use an eventChannel: it lets you connect your saga to a source of events external to the redux ecosystem.

First create your eventChannel using the provided factory function: it hands you an emit function that you can use to emit events; then consume these events using the take effect.

import { eventChannel } from 'redux-saga';
import { cancelled, take } from 'redux-saga/effects';

// first create your eventChannel
const authEventsChannel = eventChannel( emit => {
  const unsubscribe = firebaseAuth().onAuthStateChanged( user => {
    emit({ user });
  });
  // return a function that can be used to unregister listeners when the saga is cancelled
  return unsubscribe;
});

// then monitor those events in your saga
try {
  while (true) {
    const { user } = yield take (authEventsChannel);
    // handle auth state
  }
} finally {
  // unregister listener if the saga was cancelled
  if (yield cancelled()) authEventsChannel.close();
}

Upvotes: 3

Related Questions