【问题标题】:Is it a good design, when a React Flux store emits multiple kinds of events?当 React Flux store 发出多种事件时,这是一个好的设计吗?
【发布时间】:2015-09-14 08:30:45
【问题描述】:

我发现的几乎所有关于通量的教程每个商店只发出一个事件 (emitChange)。我真的不知道这是故意的,还是只是教程简单的结果。

我尝试实现一个与 CRUD 架构相对应的存储,我想知道为每个 CRUD 方法发出不同的事件是否是一个好的设计决策。

我的一家商店的相关部分如下所示:

var UserStore = _.extend({}, EventEmitter.prototype, {

    emitChange: function() {
        this.emit('change');
    },

    emitUserAdded: function() {
        this.emit('userAdded');
    },

    emitUserUpdated: function() {
        this.emit('userUpdated');
    },

    emitUserDeleted: function() {
        this.emit('userDeleted');
    },

    // addListener, removeListener in the same manner
});

如果我的方法是错误的,我将如何告诉我的组件发生的事件类型(例如:删除或更新)

【问题讨论】:

  • 我不熟悉react,但总的来说,主要考虑因素是平衡编写大量样板连接代码以为每个实体提供离散事件类型与每个更新事件处理程序每当发布 update 时触发,而不是在发布 userUpdated 时触发一个事件处理程序。您的运行时环境有多少马力?
  • '您的运行时环境有多少马力?' - 这个问题是什么意思?
  • 我认为有一个更新事件是不合适的,因为每个商店都代表一个独立的实体。所以事件必须来自例如 UserStore,所以我无法触发一般更新事件。但是,我可以从我的 UserStore 触发一个简单的更改事件,并作为参数给出它是更新还是其他。我只是不知道这是否是最好的方法。
  • “多少马力”是一个汽车类比......它的意思是“运行时间有多强大或有能力”。如果您在服务器上的节点中运行,则与在最低公分母用户的浏览器上运行相比,您拥有更多的“马力”。
  • 显然我是在浏览器中运行它的 :) 但我不认为性能会成为这里的瓶颈,我只是问,这是否是一个好的设计决策(来自代码质量角度)。

标签: reactjs reactjs-flux


【解决方案1】:

Flux 作为一种设计模式是建立在所有数据都驻留在“存储”中的想法之上的。每个商店都保存给定信息域的数据。例如:在 Flux 中,所有 cmets 都将驻留在 CommentStore 中。

当存储中的数据发生更改时,它应该发出一个事件,并且构建在这个“信息域”之上的所有组件都应该重新呈现并显示新的域数据。

我发现,当商店发出多种事件类型时,组件更有可能没有侦听该特定事件,因此当域数据发生更改时不会重新呈现自身。

这打破了整个通量模式,并且很容易造成组件与商店信息不同步的难以发现的错误。

我建议您根据“得墨忒耳法则”设计组件 - 每个组件只应了解其需要的信息。

因此,您应该创建一个commentListComponent 来监听单个商店事件,而不是让组件监听“commentList 已更新”的事件。因此,组件将监听 commentStore.on('change') - 我通常让所有商店发出一个 'change' 事件。当商店发出时,您应该重新渲染 commenListComponent 中的数据以反映商店。如果你使用 React,这就是你使用 setState 的地方。

var commentStore = _.extend({}, EventEmitter.prototype, {

updateComents: function() {
    // Update comments and emit
    this.emit('change');
},

removeComments: function() {
    // Remove comments and emit
    this.emit('change');
},

getState: function() {
    return {
        comments: this.comments,
        someOtherDomainData: this.meta,
    }
}
});

//commentListComponent.js
var commentListComponent = React.createClass({
    componentDidMount : function() {
        commentStore.on('change', this._commentChanged);
    },
    componentWillUnmount : function() {
        commentStore.off('change', this._commentChanged);
    },
    _commentChanged : function() { 
        this.setState({ comments : commentStore.getState().comments });
    },
    render : function() {
        var comments = // Build a list of comments.
        return <div>{comments}</div>
    }
})

这使得数据流更加简单,并且避免了难以发现的错误。

【讨论】:

  • 感谢您的回答。但是,我不清楚如何区分此架构中的事件类型。例如,假设用户删除了一条评论。如何告诉组件删除成功?
  • 你不应该。这才是重点。让 store 处理所有的逻辑,让组件只渲染 store 返回的状态。渲染列表中所有 cmets 的组件不需要知道是否删除了评论,它只需要使用所有 cmets 重新渲染自己。
  • 好的,我看到 CommentList 组件不必知道这一点。但是想象一个弹出组件,它通知用户删除成功(将其命名为 CommentPopup)。所以这个组件需要一些事件,告诉它发生了删除,你将如何解决它?
  • 我解决了一个类似的问题(在操作完成后显示弹出窗口),方法是让操作返回一个承诺并将触发弹出窗口的代码绑定到承诺的“成功”处理程序。例如,“删除评论”按钮 onclick 处理程序将包含:CommentActions.deleteComment(id).then(() =&gt; { this.setState({showDeletedPopup: true}) })。我不知道这是否符合 Flux 工作流程的习惯,但它确实有效。
  • TeChn4K - 这取决于您的具体设置。正如您所说,刷新很昂贵,尤其是在刷新 DOM 时。 Flux 从来没有建立在一切都应该重新渲染的想法上——只是从程序员的角度来看,它应该看起来像一切都重新渲染。因此,Flux 在使用虚拟 dom 的环境中蓬勃发展。这是因为虚拟 dom(react、vue 等)最大限度地减少了必要的重新渲染次数。如果虚拟 dom 无法开箱即用地对此进行充分优化,您通常可以在适合刷新其数据时提示框架
【解决方案2】:

我的用例迫使我使用不同的事件。我有一个地图组件,它显示了许多复杂的对象,刷新每个对象会非常昂贵。

EventEmitter2 实现多级和通配符事件。它允许我监听“更改”事件,或者更具体地说是子级别事件“change.add”“change.remove”...

this.emit("change.add");

可以收听

myStore.on('change.*', callback);
myStore.on('change.add', callback);

这样,我的很多组件就可以简单的监听“change.*”事件了。而需要优化的更复杂的组件可以监听特定的事件。

两全其美

PS:不要忘记启用通配符。查看文档

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    • 2011-09-13
    • 1970-01-01
    • 2020-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多