Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React/Flux way to handle permission sensitive actions with login flows

I have been playing with React/Flux, and I am having trouble wrapping my head around the 'Flux Way' for handling permission-sensitive actions.

Overarching question: When a non-logged in visitor attempts an action that requires he/she to be logged in, what is the Flux way of (a) checking if a user is logged in, (b) starting the login flow, (c) finishing the action on success?

Take an example of a forum app, that requires users to be logged in to post:

We have a comment form component (mostly taken from FB's React tut) as such:

var CommentForm = React.createClass({
  handleSubmit: function ( e ) {
    e.preventDefault();

    // get data
    commentData = {
      content: this.refs.content.getDOMNode().value.trim()
    };

    this.props.onCommentSubmit( commentData );
    this.resetForm();
  },

  resetForm: function () {
    this.refs.content.getDOMNode().value = '';
  },

  render: function () {
    return (
      <form className="comment-form" id="comment-form" onSubmit={ this.handleSubmit }>
        <textarea name="comment[content]" placeholder="What's on your mind?" ref="content"></textarea>
        <button className="post-comment button" type="submit">Post</button>  
      </form>
    )
  }
});

A comments store (abbreviated)

var CHANGE_EVENT = 'change';
var _comments = {};

function createComment ( data ) {
  // post to server
}

var CommentStore = React.addons.update(EventEmitter.prototype, {$merge: {

  // omitted methods

  dispatcherIndex: AppDispatcher.register(function(payload) {
    var action = payload.action;
    var text;

    switch(action.actionType) {
      case CommentConstants.ADD_COMMENT:
        AppDispatcher.waitFor([SessionStore.dispatchToken])
        createComment(action.data);
        break;
    }

    return true;
  })
}});

And a store for handling sessions (also abbreviated):

var CHANGE_EVENT = 'change';

function ensureCurrentUser () {
  if ( !SessionStore.currentUser() ) {
    app.router.navigate('/login');
  }
}

var SessionStore = React.addons.update(EventEmitter.prototype, {$merge: {

  // omitted code

  currentUser: function () {
    return app.context.current_user;
  },

  dispatchToken: AppDispatcher.register(function ( payload ) {
    var action = payload.action;

    switch(action.actionType) {
      case CommentConstants.ADD_COMMENT:
        ensureCurrentUser();
        break;
    }

    return true;
  })
}});

My initial thought was that this is a case for Flux's waitFor() method. However, the implementation above fails, as waitFor closes the dependency loop as soon as SessionStore.dispatchToken is set (as soon as ensureCurrentUser returns).

Is this a case where the payload should be passed into ensureCurrentUser, and then into the route handler for /login? What is the Flux way of handling these types of flows?

Thanks in advance :)

like image 954
mattmattmatt Avatar asked Oct 12 '14 19:10

mattmattmatt


2 Answers

As FakeRainBrigand suggested in his answer, you'd want to just check that the user has a valid session before creating a comment by first retrieving that value from the SessionStore:

case CommentConstants.ADD_COMMENT:
  if (SessionStore.getUser()) {
   createComment(action.data);
  }
  break;

But to preserve the comment so that it gets created after the user logs in, I would create a collection of pending comments in the CommentStore, and then later, in a callback to the login verification and session creation, dispatch a new action to inform the CommentStore that the user has now logged in. Then the CommentStore can respond to that by creating real comments out of the pending ones.

like image 135
fisherwebdev Avatar answered Oct 05 '22 23:10

fisherwebdev


The simplest would be just asking SessionStore if there's a session.

 case CommentConstants.ADD_COMMENT:
    if (SessionStore.getUser()) {
       createComment(action.data);
    }
    break;

Of course, this just saves you a server request. The behavior shouldn't be any different than always calling createComment(action.data).

Is this a case where the payload should be passed into ensureCurrentUser, and then into the route handler for /login? What is the Flux way of handling these types of flows?

For this, you might want to have an event emitter that emits a 'login' event.

 case CommentConstants.ADD_COMMENT:
    if (SessionStore.getUser()) {
       createComment(action.data);
    }
    else {
       someEventEmitter.one('login', function(){
           createComment(action.data);
       });
       someEventEmitter.emit('loginRequired');
    }
    break;

And when loginRequired is emitted, if there isn't a user logged in, the login view would be presented.

like image 29
Brigand Avatar answered Oct 05 '22 23:10

Brigand