【问题标题】:Emitting and handling global events with react使用 react 发送和处理全局事件
【发布时间】:2015-02-12 18:26:15
【问题描述】:

我正在使用 react 构建一个“添加到购物车按钮”。这是我的代码。

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>
    );
  }
});

我很好奇的是这个 ajax 处理程序。我很确定反应的重点是组件之间的互操作性,但我不知道将这些事件引向何处。我可以想象几个不同的组件,例如成功时的购物车计数指示器或失败时的错误警报,但我不完全知道如何利用这些组件。这就是flux's dispatchers 的重点吗?

【问题讨论】:

    标签: javascript reactjs reactjs-flux


    【解决方案1】:

    是的,它肯定是 Flux 调度程序的一部分——或者您想使用的任何事件发射器。

    不过,在你走这条路之前,很容易将事件处理程序作为道具传递,而无需使用 Flux 或自定义事件发射器——就像你对普通 DOM 元素使用 onSubmitonClick 等处理程序一样。然后让父母处理设置状态,并可能将其传达给其他孩子(通过道具)。

    所以在这种情况下,想象一个处理事件的父组件:

    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} />
        )
      }
    })
    

    然后您的ProductPurchase 组件的相关部分将是:

      success: function(data) {
        this.props.onCartAdded(data)
      }.bind(this),
      error: function(xhr, status, err) {
        this.props.onError(err)
      }.bind(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>
        )
      }
    })
    

    通过这种方式,组件相互解耦——数据/函数只能通过明确的合约(props)传入。

    这种简单的事件处理风格更加明确且更易于调试 - 因此,如果您的应用程序变得非常复杂和/或您有很多组件都需要,我真的只会求助于 Flux 风格的架构以复杂的方式相互交流。

    【讨论】:

      【解决方案2】:

      Flux 用于解耦编程结构(包括 AJAX 调用)。

      这是来自Flux Docs的图表

      Flux 架构中的

      Dispatcher 始终保持在全局范围内。因此,与调度程序相关的任何操作总是发生在全局范围内。另外,dispatcher 和 event-system 有细微的差别,event system 总是注册绑定到特定事件的回调,但在 dispatcher 的情况下,所有回调都绑定到所有事件。

      如何使用调度程序? (不使用 Store 和 ActionCreators 的简化方法)

      1. 如果应用程序的其他部分受到此 AJAX 调用的影响,那么您不应从此组件进行此 AJAX 调用,而应将 AJAX 调用移至新文件和函数中。例如使用 CommonJS,

        // CartApiUtils.js
        module.exports = {
            addToCart: function(item){
                // AJAX call
            }
        }
        
      2. 使用 Flux 的 Dispatcher class 创建一个 AppDispatcher(在整个应用程序中很常见)

        var appDispatcher = new Dispatcher();
        
      3. 在 addToCart() 函数中,在成功的 AJAX 响应时,使用 AppDispatcher 调度事件:

        appDispatcher.dispatch({
            actionType: 'cart.newItemAdded',
            data: dataFromAjax
        });
        
      4. 在你的应用中,无论你想在哪里使用这个事件,你都可以注册一个函数到dispacher。

        appDispatcher.register(function(payload){
            if(payload.actionType === 'cart.newItemAdded'){
                // payload.data contains the data
            }
        });
        

      这是一种简化的方法。在更规范化的结构和更大的应用程序中,您应该使用 Stores(类似于 MVC 的模型层,但不完全相同)和 ActionCreator,其中视图层上的任何交互都是用户的操作和 AJAX 调用的任何响应也成为服务器的一个动作。

      Thumb 规则是,必须从 Store 填充(或更新)视图,并且必须根据 Dispatcher 事件更新 Store。

      【讨论】:

      • 注意:不要使用您在组件中链接的调度程序,因为它无法取消注册,这意味着无法在 componentWillUnmount 中进行清理。如果您希望在没有存储的情况下使用它,只需使用普通的事件发射器(例如节点中的 events.EventEmitter)。
      • 如何在每个模块中获得对单例 Dispatcher 的引用?
      • 您需要将分派状态存储在包裹两个屏幕的上下文中
      【解决方案3】:

      使用 npm 的“react-global-events”怎么样?

      https://www.npmjs.com/package/react-global-events

      【讨论】:

        【解决方案4】:

        Flux 是维护模式,所以如果您打算构建一个生产就绪的应用程序,我不建议您这样做。替代方案是 Redux 或 MobX,不过我总是选择 React's builtin Context 来处理全局状态 - 易于实现并且工作起来就像一个魅力。

        【讨论】:

          猜你喜欢
          • 2017-12-02
          • 1970-01-01
          • 2020-10-23
          • 2015-04-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多