Feature Request: Calling an Action returns a Promise #112

Closed
echenley opened this Issue Nov 6, 2014 · 9 comments

Comments

Projects
None yet
5 participants
@echenley

echenley commented Nov 6, 2014

It would be very handy to have the ability to execute functions on completion of an Action, allowing nice things like executing UI code inline:

postActions.submitPost(post).then(function() {
    this.transitionTo('home');
}.bind(this));

Promises would ideally be resolved when a Store triggers an event. This would allow for asynchronous behavior in the Stores:

var postStore = Reflux.createStore({
    init: function () {
        this.posts = {};
        databaseRef.on('child_added', function(posts) {
            this.posts = posts.val();
            this.trigger(this.posts); // promise is resolved here
        }.bind(this));
    },
    submitPost: function (post) {
        // asynchronous push to database
        databaseRef.push(post);
    }
});
@WRidder

This comment has been minimized.

Show comment
Hide comment
@WRidder

WRidder Nov 6, 2014

Interesting idea. However, if I'm correct, you have to be careful to not undermine the idea of a single data flow. By this I mean that most uses of promises may prevent the (proper?) use of actions.

For example, let's take the submit post use case where I would normally use two actions: submitPost and postSaved for instance to track this async backend request. Using promises I could just use the promise returned by the submitPost action.

I'm new to Reflux and still figuring it out so I might very well be wrong about this.

WRidder commented Nov 6, 2014

Interesting idea. However, if I'm correct, you have to be careful to not undermine the idea of a single data flow. By this I mean that most uses of promises may prevent the (proper?) use of actions.

For example, let's take the submit post use case where I would normally use two actions: submitPost and postSaved for instance to track this async backend request. Using promises I could just use the promise returned by the submitPost action.

I'm new to Reflux and still figuring it out so I might very well be wrong about this.

@echenley

This comment has been minimized.

Show comment
Hide comment
@echenley

echenley Nov 6, 2014

I was thinking the same thing with regard to data flow. Changed it to a userStore example because that makes more sense. After some wiggling, I figured out that a more Reflux-y way of doing it is this:

var React = require('react/addons');
var Reflux = require('reflux');

var Navigation = require('react-router').Navigation;
var userActions = require('../actions/userActions');
var userStore = require('../stores/userStore');

var Login = React.createClass({

    mixins: [Navigation, Reflux.ListenerMixin],

    componentDidMount: function () {
        this.listenTo(userStore, function () {
            this.transitionTo('home');
        }.bind(this));
    },

    signIn: function (e) {
        e.preventDefault();
        userActions.signIn({
            email: this.refs.email.getDOMNode().value.trim(),
            password: this.refs.password.getDOMNode().value.trim()
        });
    },

    // render, etc.
});

echenley commented Nov 6, 2014

I was thinking the same thing with regard to data flow. Changed it to a userStore example because that makes more sense. After some wiggling, I figured out that a more Reflux-y way of doing it is this:

var React = require('react/addons');
var Reflux = require('reflux');

var Navigation = require('react-router').Navigation;
var userActions = require('../actions/userActions');
var userStore = require('../stores/userStore');

var Login = React.createClass({

    mixins: [Navigation, Reflux.ListenerMixin],

    componentDidMount: function () {
        this.listenTo(userStore, function () {
            this.transitionTo('home');
        }.bind(this));
    },

    signIn: function (e) {
        e.preventDefault();
        userActions.signIn({
            email: this.refs.email.getDOMNode().value.trim(),
            password: this.refs.password.getDOMNode().value.trim()
        });
    },

    // render, etc.
});
@echenley

This comment has been minimized.

Show comment
Hide comment
@echenley

echenley Nov 6, 2014

In my case, posts are a little more tricky since the postStore triggers an event every time a new post is added to the Store; it's not necessarily the user's action that caused it. One solution might be a separate userPostStore to trigger user-specific Post events?

echenley commented Nov 6, 2014

In my case, posts are a little more tricky since the postStore triggers an event every time a new post is added to the Store; it's not necessarily the user's action that caused it. One solution might be a separate userPostStore to trigger user-specific Post events?

@WRidder

This comment has been minimized.

Show comment
Hide comment
@WRidder

WRidder Nov 6, 2014

That might indeed be a decent approach. Since you can indeed receive posts from the backend for instance (using sockets/polling).

Tomorrow I'll start working on setting up a sample application which should resemble a small community website (so more involved than the basic todo app). Think about pages, discussions and the like. I would like to figure out some best practices using reactjs + reflux with regards to routing, permissions, pages, real-time data (sockets), server-side rendering. Just to cover all the basics. Will keep you guys posted.

WRidder commented Nov 6, 2014

That might indeed be a decent approach. Since you can indeed receive posts from the backend for instance (using sockets/polling).

Tomorrow I'll start working on setting up a sample application which should resemble a small community website (so more involved than the basic todo app). Think about pages, discussions and the like. I would like to figure out some best practices using reactjs + reflux with regards to routing, permissions, pages, real-time data (sockets), server-side rendering. Just to cover all the basics. Will keep you guys posted.

@krawaller

This comment has been minimized.

Show comment
Hide comment
@krawaller

krawaller Nov 7, 2014

Contributor

It's not prepped for public eyes, but here's the innards of a recent app of mine; I have a loginStore which triggers with a username or undefined whenever the user logs in or out, and the loginButton then listens to that store (as does some other components).

I agree with the sentiment that a Store should control just one thing. My app also has a userStore, a chatStore, etc.

You can see the app live here, but as you can't login since it combines Github auth with checking against a hardcoded array, some of the functionality will be untestable to you (logged in users can chat and edit their info).

Maybe too general, but I also wrote a post on the Reflux data flow which might help.

Contributor

krawaller commented Nov 7, 2014

It's not prepped for public eyes, but here's the innards of a recent app of mine; I have a loginStore which triggers with a username or undefined whenever the user logs in or out, and the loginButton then listens to that store (as does some other components).

I agree with the sentiment that a Store should control just one thing. My app also has a userStore, a chatStore, etc.

You can see the app live here, but as you can't login since it combines Github auth with checking against a hardcoded array, some of the functionality will be untestable to you (logged in users can chat and edit their info).

Maybe too general, but I also wrote a post on the Reflux data flow which might help.

@WRidder

This comment has been minimized.

Show comment
Hide comment
@WRidder

WRidder Nov 8, 2014

@krawaller thanks! That makes for some nice reference. Thumbs up for the blog posts as well, good reads.

WRidder commented Nov 8, 2014

@krawaller thanks! That makes for some nice reference. Thumbs up for the blog posts as well, good reads.

@echenley

This comment has been minimized.

Show comment
Hide comment
@echenley

echenley Nov 8, 2014

👍 Thanks y'all.

echenley commented Nov 8, 2014

👍 Thanks y'all.

@spoike

This comment has been minimized.

Show comment
Hide comment
@spoike

spoike Nov 10, 2014

Member

I guess everyone is in agreement, that you should rely on change events from stores instead.

While it'd be nice to see when an action is done, it's much more flux:y to wait for the affected stores to update instead of when the action is completely done. It's more in line with data-flow thinking where components and stores only care about data coming in and occasionally initiate new data flows by invoking actions.

I'll close this for now.

Member

spoike commented Nov 10, 2014

I guess everyone is in agreement, that you should rely on change events from stores instead.

While it'd be nice to see when an action is done, it's much more flux:y to wait for the affected stores to update instead of when the action is completely done. It's more in line with data-flow thinking where components and stores only care about data coming in and occasionally initiate new data flows by invoking actions.

I'll close this for now.

@spoike spoike closed this Nov 10, 2014

@ericclemmons

This comment has been minimized.

Show comment
Hide comment
@ericclemmons

ericclemmons Dec 30, 2014

Contributor

@spoike I originally agreed with the logic, but it makes sense to utilize a promise particularly with react-router:

https://github.com/rackt/react-router/blob/master/docs/api/components/RouteHandler.md#static-lifecycle-methods

var Person = React.createClass({
  statics: {
    willTransitionTo: function(transition, params) {
      transition.wait(PersonStore.fetch({ id: params.id }));
    }
  },

  getInitialState: function() {
    return {
      person: PersonStore.find({ id: this.getParams().id }),
    };
  },
  ...
});

It's worth strongly encouraging actions to be the primary method of communication, but this would effectively allow people to do the equivalent of angular-router's resolve with Reflux + React.

Contributor

ericclemmons commented Dec 30, 2014

@spoike I originally agreed with the logic, but it makes sense to utilize a promise particularly with react-router:

https://github.com/rackt/react-router/blob/master/docs/api/components/RouteHandler.md#static-lifecycle-methods

var Person = React.createClass({
  statics: {
    willTransitionTo: function(transition, params) {
      transition.wait(PersonStore.fetch({ id: params.id }));
    }
  },

  getInitialState: function() {
    return {
      person: PersonStore.find({ id: this.getParams().id }),
    };
  },
  ...
});

It's worth strongly encouraging actions to be the primary method of communication, but this would effectively allow people to do the equivalent of angular-router's resolve with Reflux + React.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment