I'm playing around a bit with react to build an "Add to cart button". Here's my code.
var ProductPurchase = React.createClass({ handleSubmit: function(e){ e.preventDefault(); $.ajax({ url: "/cart/add.js", method: "post", dataType: "json", data: { "id": this.props.variantId, "quantity": this.props.quantity, }, success: function(data) { // emit cart added event }.bind(this), error: function(xhr, status, err) { // emit error event (cart added) }.bind(this) }); }, getDefaultProps: function(){ return { quantity: 1, variantId: 231634908, buttonText: "Add to cart" } }, render: function() { return ( <div className="productPurchase"> <form action="/cart/add" method="post" enctype="multipart/form-data" onSubmit={this.handleSubmit}> <input type="hidden" name="quantity" value={ this.props.quantity } /> <input type="hidden" name="id" value={ this.props.variantId } /> <button type="submit">{this.props.buttonText}</button> </form> </div> ); } });
What I'm curious about is this ajax handler. I'm pretty sure the whole point of react is interoperability between components, except I don't know where to lead these events off to. I could imagine a couple of different components like a cart count indicator if success or a error alert if failure but I don't exactly know how to tap into these. Is this the whole point of flux's dispatchers?
Handling events with React elements is very similar to handling events on DOM elements. There are some syntax differences: React events are named using camelCase, rather than lowercase. With JSX you pass a function as the event handler, rather than a string.
First, I need to define the event emitter, which is simply an object that we can import to other components as needed. Next, I want to import EventEmitter to components that will be subscribing to an event and then set up the subscribe functionality. The name of the event that you pass can be whatever you like.
Event Emitter is useful library in React apps- on, adds listener and start listening on specific events, - once, like above but after first event occurence is removing, - off, removes listener for specific event type, - emit, invokes event and can add payload when is needed.
Yes, it's certainly part of the point of Flux's dispatchers – or any event emitter that you wanted to use.
Before you go down that path though, it's very easy to just pass down event handlers as props without using Flux or custom event emitters – just as you would with onSubmit
, onClick
, etc handlers for normal DOM elements. Then have the parent deal with setting the state, and potentially communicating that to other children (via props).
So in this case, imagine a parent component that deals with the events:
var RootComponent = React.createClass({ handleCartAdded: function(cart) { console.log('Got a new cart: ' + cart); } handleError: function(err) { console.error(err) } render: function() { return ( <ProductPurchase onCartAdded={this.handleCartAdded} onError={this.handleError} /> ) } })
And then the relevant part of your ProductPurchase
component would be:
success: function(data) { this.props.onCartAdded(data) }.bind(this), error: function(xhr, status, err) { this.props.onError(err) }.bind(this)
A more complex example would be to pass the result to another child component – but again, leave it up to the parent to manage this:
var RootComponent = React.createClass({ handleCartAdded: function(cart) { this.setState({cart: cart}) } handleError: function(err) { console.error(err) } render: function() { return ( <div> <ProductPurchase onCartAdded={this.handleCartAdded} onError={this.handleError} /> <CartSummary cart={this.state.cart} /> </div> ) } })
This way, the components are decoupled from each other – and data/functions can only be passed in by a clear contract (props).
This simple style of event handling is a lot more explicit and easier to debug – so I would really only resort to a Flux style architecture if your app is getting really complex and/or you have a lot of components that all need to communicate with each other in a complex manner.
Flux is used to decouple programming constructs (including AJAX calls).
Here is the diagram from the Flux Docs
Dispatcher in Flux architecture always remains in Global scope. So any operation involved with the dispatcher always occurs in the global scope. Also, dispatcher and event-system has a slight difference, event system always registers callbacks bound to a specific event but in case of dispatchers, all the callbacks are bound to all the events.
How to use Dispatcher? (Simplified approach without the use of Stores and ActionCreators)
If other parts of application is affected by this AJAX call then you shouldn't make this AJAX call from this component, rather move the AJAX call into a new file and a function. Eg using CommonJS,
// CartApiUtils.js module.exports = { addToCart: function(item){ // AJAX call } }
Create an AppDispatcher (which is common throughout the application) using Flux's Dispatcher class
var appDispatcher = new Dispatcher();
In addToCart() function, on successful AJAX response, dispatch an event using AppDispatcher:
appDispatcher.dispatch({ actionType: 'cart.newItemAdded', data: dataFromAjax });
In your app, wherever you want to use this event, you can just register a function to dispacher.
appDispatcher.register(function(payload){ if(payload.actionType === 'cart.newItemAdded'){ // payload.data contains the data } });
This is a simplified approach. In a more normalized structure and larger app, you should use Stores (which is something like the Model layer of MVC, but not identical) and ActionCreator where any interaction on the view layer is an action by the user and any response by the AJAX call also becomes an action from the Server.
Thumb rule is that, Views must be populated (or updated) from Stores and Stores must be updated on Dispatcher events.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With