Ambiguous docs / usage for Component -> Store listeners #105

Closed
damassi opened this Issue Oct 25, 2014 · 8 comments

Comments

Projects
None yet
3 participants
@damassi

damassi commented Oct 25, 2014

I'm testing out Reflux as a way to simplify my React flow and I'm finding that the docs are somewhat ambiguous about how to best listen to store updates in react components.

In the Store example you provide, its a fairly clear and explicit one-to-one which maps to the common addEventListener(event, method) pattern:

var Store = Reflux.createStore({
    init: function() {
        this.listenTo(actions.fireBall,this.onFireBall);
        this.listenTo(actions.magicMissile,this.onMagicMissile);
    },
    onFireBall: function(){
        // whoooosh!
    },
    onMagicMissile: function(){
        // bzzzzapp!
    }
});

But within the Component it is not so clear, and the docs provide no further clarification of the conventions that you're using:

// Fairly simple view component that outputs to console
function ConsoleComponent() {

    // Registers a console logging callback to the statusStore updates
    statusStore.listen(function(status) {
        console.log('status: ', status);
    });
};

and

var Status = React.createClass({
    mixins: [Reflux.ListenerMixin],
    onStatusChange: function(status) {
        this.setState({
            currentStatus: status
        });
    },
    componentDidMount: function() {
        this.listenTo(statusStore, this.onStatusChange);
    },
    render: function() {
        // render specifics
    }
});

What happens if the store that the component is listening to has multiple methods? Did you design Reflux to only support one store method per component? From the above it looks like the mixin is magically connecting events to methods created without explicit intent where the normal pattern (say, with Backbone) is this.listenTo(model, event, method). The above examples could be written like so, which would be much clearer in terms of intent.

Lastly, here's a better illustration of the problem:

var CommentComponent = React.createClass({
  mixins: [Reflux.ListenerMixin],

  componentDidMount: function () {

    // Problem 1:  
    // How do you discern the difference between onFetchComments 
    // and onPostComments? Right now our component is just listening
    // to the CommentStore and lots of stuff might be going on.  When
    // onFetchComments is called, so is onPostComment because it gets
    // confused.  How did you envision we should listen to multiple events?

    this.listenTo(CommentStore, this.onFetchComments);
    this.listenTo(CommentStore, this.onPostComment);

    // Problem 2:  
    // This is a better implementation, but it breaks if the user is making
    // any sort of async ajax request in the store as it appears to bypass the store completely.  
    //  I cant figure out if there's something going on in
    // my code or if there is a bug in the codebase, or if I'm just misunderstanding completely.

    this.listenTo(CommentAction.fetchComments, this.onFetchComments);
    this.listenTo(CommentAction.onPostComment, this.onFetchComments);
  },

})

Another thing that would be nice is if you had actual API docs as opposed to just usage examples. Like... when you include the Reflux.ListenerMixin, what methods does it supply to the Component? What is the method signature for this.listenTo?.

@damassi damassi changed the title from Ambiguous documentation for Reflux.ListenerMixin and Component > Store listeners in general to Ambiguous docs / usage for Reflux.ListenerMixin and Component > Store listeners in general Oct 25, 2014

@damassi damassi changed the title from Ambiguous docs / usage for Reflux.ListenerMixin and Component > Store listeners in general to Ambiguous docs / usage for Component -> Store listeners in general Oct 25, 2014

@damassi damassi changed the title from Ambiguous docs / usage for Component -> Store listeners in general to Ambiguous docs / usage for Component -> Store listeners Oct 25, 2014

@damassi

This comment has been minimized.

Show comment
Hide comment
@damassi

damassi Oct 26, 2014

From digging around examples on github it seems the best way to handle this is to send back the entire store after the change so that state can be updated in batch and things can remain in sync. This is actually a much better way to do it and so I'll close. Sorry!

damassi commented Oct 26, 2014

From digging around examples on github it seems the best way to handle this is to send back the entire store after the change so that state can be updated in batch and things can remain in sync. This is actually a much better way to do it and so I'll close. Sorry!

@damassi damassi closed this Oct 26, 2014

@krawaller

This comment has been minimized.

Show comment
Hide comment
@krawaller

krawaller Oct 26, 2014

Contributor

Apologies if I misunderstand, but I think you're missing a fundamental Reflux principle; a publisher (store, action or other object consuming the publisher API) is only considered to transmit one single type of event. When the publisher calls its trigger method such an event is transmitted, and heard by all who are listening to the publisher.

Thus a single publisher cannot send both onFetchComments and onPostComments. Presumably the first should be sent by a store and the second by an action.

When a listener calls .listenTo you're only selecting which callback to catch the event with, not what method on the publisher to listen to. You're always listening to trigger.

This all might seem limiting, and it is, but its also the reason why Reflux structures become way easier to reason about.

Contributor

krawaller commented Oct 26, 2014

Apologies if I misunderstand, but I think you're missing a fundamental Reflux principle; a publisher (store, action or other object consuming the publisher API) is only considered to transmit one single type of event. When the publisher calls its trigger method such an event is transmitted, and heard by all who are listening to the publisher.

Thus a single publisher cannot send both onFetchComments and onPostComments. Presumably the first should be sent by a store and the second by an action.

When a listener calls .listenTo you're only selecting which callback to catch the event with, not what method on the publisher to listen to. You're always listening to trigger.

This all might seem limiting, and it is, but its also the reason why Reflux structures become way easier to reason about.

@damassi

This comment has been minimized.

Show comment
Hide comment
@damassi

damassi Oct 26, 2014

Totally. I've been up wayyy too long now and suddenly realized the enforced constraint. With the standard Flux example its very explicit that you're sending back your updated store which is then consumed by the component. The fact that this wasn't made clear in the Reflux docs sent me for a bit of a loop in thinking that there was simply some part of the api that i was missing.

Thanks so much for the lib. I'm really digging its simple clarity.

damassi commented Oct 26, 2014

Totally. I've been up wayyy too long now and suddenly realized the enforced constraint. With the standard Flux example its very explicit that you're sending back your updated store which is then consumed by the component. The fact that this wasn't made clear in the Reflux docs sent me for a bit of a loop in thinking that there was simply some part of the api that i was missing.

Thanks so much for the lib. I'm really digging its simple clarity.

@krawaller

This comment has been minimized.

Show comment
Hide comment
@krawaller

krawaller Nov 4, 2014

Contributor

Your question caused me to write a description of the data flow approach. Hope it helps!

Contributor

krawaller commented Nov 4, 2014

Your question caused me to write a description of the data flow approach. Hope it helps!

@damassi

This comment has been minimized.

Show comment
Hide comment
@damassi

damassi Nov 4, 2014

Awesome :). I am totally loving reflux btw. Thanks for the great work.

damassi commented Nov 4, 2014

Awesome :). I am totally loving reflux btw. Thanks for the great work.

@marconi

This comment has been minimized.

Show comment
Hide comment
@marconi

marconi Jan 24, 2015

But what happens say on the store you send an ajax request to save the comment, when something goes wrong how do you notify the component with error message?

marconi commented Jan 24, 2015

But what happens say on the store you send an ajax request to save the comment, when something goes wrong how do you notify the component with error message?

@krawaller

This comment has been minimized.

Show comment
Hide comment
@krawaller

krawaller Jan 30, 2015

Contributor

There are several approaches to tackle that. One way would be to have the store call a "commentsavefailed" action which the component is listening to. If it is a more complex app I sometimes have an "errorStore" which listens to error actions called by other stores, and then my component(s) listen to the errorStore.

Contributor

krawaller commented Jan 30, 2015

There are several approaches to tackle that. One way would be to have the store call a "commentsavefailed" action which the component is listening to. If it is a more complex app I sometimes have an "errorStore" which listens to error actions called by other stores, and then my component(s) listen to the errorStore.

@marconi

This comment has been minimized.

Show comment
Hide comment
@marconi

marconi Jan 30, 2015

Thanks @krawaller, yeah ended up with response specific actions which indicates if its success or not.

marconi commented Jan 30, 2015

Thanks @krawaller, yeah ended up with response specific actions which indicates if its success or not.

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