Mike Perrenoud
Mike Perrenoud

Reputation: 67898

React Flux Store emitChange Not Firing in Both Components

I have two different components that leverage the same AuthStore, FacebookUser and Quiz.

The FacebookUser component updates the AuthStore by executing the login action and that actions causes it to execute emitChange. However, the onChange handler for the Quiz does not fire.

AuthStore.js

console.log("AuthStore::CREATED");

var AppDispatcher = require('../dispatcher/AppDispatcher');
var EventEmitter = require('events').EventEmitter;
var AuthConstants = require('../constants/AuthConstants');
var assign = require('object-assign');

var CHANGE_EVENT = 'change';

var _user = undefined;

var AuthStore = assign({}, EventEmitter.prototype, {

  isLoggedIn: function() {
    return _user !== undefined;
  },

  getUser: function() {
    return _user;
  },

  emitChange: function() {
    console.log('AuthStore::emitChange');
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function(callback) {
    console.log('AuthStore::addChangeListener', callback);
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
  }
});

// Register callback to handle all updates
AuthStore.dispatchToken = AppDispatcher.register(function(action) {
  console.log('AuthStore::action', action);

  switch(action.actionType) {
    case AuthConstants.AUTH_LOGIN:
      _user = action.user;
      AuthStore.emitChange();
      break;

    case AuthConstants.AUTH_LOGOUT:
      _user = undefined;
      AuthStore.emitChange();
      break;

    default:
      // no op
  }
});

module.exports = AuthStore;

FacebookUser.jsx

var AuthActions = require('../../actions/AuthActions');
var AuthStore = require('../../stores/AuthStore');

function componentWillMount() {
  AuthStore.addChangeListener(this.onChange);
}

function onChange() {
  console.log('FacebookUser::onChange');

  var user = AuthStore.getUser();
  this.setState(user);
}

function populateUserProfile(userId) {
  FB.api('/' + userId, {fields: 'email,name'}, function(response) {
    console.log('FacebookUser::populateUserProfile', response);
    AuthActions.login(response);
  }.bind(this));
}

Quiz.jsx

var AuthStore = require('../../stores/AuthStore');
var QuizStore = require('../../stores/QuizStore');

function componentWillMount() {
  AuthStore.addChangeListener(this.onChange);
  QuizStore.addChangeListener(this.onChange);
}

function onChange() {
  console.log('Quiz::onChange');

  var quiz = QuizStore.getQuiz();

  if (!quiz.person.email) {
    var user = AuthStore.getUser();
    quiz.person.email = user.email;
  }

  this.setState(quiz);
}

AppDispatcher.js

var Dispatcher = require('flux').Dispatcher;

module.exports = new Dispatcher();

I thought that the stores were Singleton's, however, it appears that's not the case. I know I'm missing something stupid. Looking forward to the answer!

Console Output

Below you can see that the FacebookUser::onChange fires. I'm pretty sure the issue is that the AuthStore is created for both components. That's pretty visible from the AuthStore::CREATED log.

AuthStore::CREATED
QuizStore::create
AuthStore::addChangeListener onChange() {
  console.log('Quiz::onChange');

  var quiz = QuizStore.getQuiz();

  if (!quiz.person.email) {
    var user = AuthStore.getUser();
    quiz.person.email = user.email;
  }

  th…
QuizStore::addChangeListener onChange() {
  console.log('Quiz::onChange');

  var quiz = QuizStore.getQuiz();

  if (!quiz.person.email) {
    var user = AuthStore.getUser();
    quiz.person.email = user.email;
  }

  th…
AuthStore::CREATED
AuthStore::addChangeListener onChange() {
  console.log('FacebookUser::onChange');

  var user = AuthStore.getUser();
  this.setState(user);
}
FacebookUser::statusChangeCallback Object {authResponse: Object, status: "connected"}
FacebookUser::populateUserProfile Object {email: "[email protected]", name: "Michael Perrenoud", id: "10209047315608853"}
AuthActions::login
AuthStore::action Object {actionType: "AUTH_LOGIN", user: Object}
AuthStore::emitChange
FacebookUser::onChange

Upvotes: 0

Views: 905

Answers (2)

Mike Perrenoud
Mike Perrenoud

Reputation: 67898

So the issue ended up being a bit esoteric. I was including this user component with a different ReactDOM renderer. Effectively like its own application. This is what was causing the problem. Because they were in different scopes they didn't share the same stores. Moving the component to being rendered by the root component for the application fixed it.

Upvotes: 0

mike james
mike james

Reputation: 9430

Rather than exposing the constructor expose an instance call new on your stores and expose that instance. That way it will be a singleton, in tests I expose a non default export so I can reinititialse a store for each test.

Upvotes: 0

Related Questions