我目前正在使用通常与ReactJS相关联的助焊剂模式开发原型应用程序。
在Facebook flux/chat example中,有两个商店,ThreadStore和UnreadThreadStore。后者提供了一个 getAll 方法,它同步读取前者的内容。
我们遇到的问题是,派生商店中的操作太昂贵而无法同步执行,理想情况下会被委托给异步进程(Web worker,服务器之旅),我们想知道如何解决这个问题
我的同事建议从getter返回一个承诺,即
# MyView
componentDidMount: function () {
defaultState = { results: [] };
this.setState(defaultState);
DerivedStore.doExpensiveThing()
.then(this.setState);
}
我对此并不十分满意。感觉就像是对模式的突破,因为视图是变化的主要接收者,而不是商店。这是我们一直在探索的另一种途径 - 视图挂载事件调度了对刷新派生数据的需求(如果需要)。
# DerivedStore
# =========================================================
state: {
derivedResults: []
status: empty <fresh|pending|empty>
},
handleViewAction: function (payload) {
if (payload.type === "refreshDerivedData") {
this.state.status = "pending"; # assume an async action has started
}
if (payload.type === "derivedDataIsRefreshed") {
this.state.status = "fresh"; # the async action has completed
}
this.state.derivedResults = payload.results || []
this.notify();
}
# MyAction
# =========================================================
MyAction = function (dispatcher) {
dispatcher.register(function (payload) {
switch (payload) {
case "refreshDerivedData":
doExpensiveCalculation()
.then(function(res) {
dispatcher.dispatch({
type: "derivedDataIsRefreshed",
results: res
})
})
);
}
});
};
# MyView
# =========================================================
MyView = React.createClass({
componentDidMount: function () {
if (DerivedStore.getState().status === "empty") {
Dispatcher.dispatch("refreshDerivedData");
}
},
getVisibility: function () {
return DerivedStore.getState().status === "pending" ? "is-visible" : ""
},
render: function () {
var state = DerivedStore.getState()
, cx = React.addons.classSet
, classes = cx({
"spinner-is-visible": state.status === "pending"
});
return <div {classes}>
<Spinner /> # only visible if "spinner-is-visible
<Results results={state.derivedResults}/> # only visible if not...
</div>;
}
});
# MyService
# =========================================================
# ensure derived data is invalidated by updates in it's source?
OriginalStore.addListener(function () {
setTimeout(function () {
dispatcher.dispatch({
type: "refreshDerivedData"
})
}, 0);
});
我喜欢这种方法的方法是视图将DerivedStore视为视图模型,此类视图主要关注其视图模型的新鲜度。然而,我担心的是商店可能会失去同步。
我的问题:
PS:对不起,如果这段代码中有任何基本的linting错误,我在过去的3个月里一直在使用Coffeescript而且它已经破坏了我的linting权力......
答案 0 :(得分:2)
所有异步操作都应该由创建操作引起。异步操作的完成应通过创建另一个操作来发出信号。商店可能会听取这些操作,并发出更改事件。
在您的组件中,您可以收听DerivedStore以进行更改。可以从任何位置创建操作,例如在组件或其他商店中。 (最终)派生数据,更新存储,发出更改事件,并且您的组件将事件有效负载应用于状态。
总而言之,您的组件实际上并不知道幕后发生的事情是同步还是异步。这很棒,因为它允许您在幕后进行这些性能更改,而不会有损坏组件的风险。
纯商店通常只有一个公共功能,它可以获得商店的状态。在你的组件中,你应该只在getInitialState中调用它,或者更好的是:有一个mixin执行此操作并为您添加更改侦听器。
答案 1 :(得分:1)
听起来像github上的以下讨论的组合可以帮助你。
store.getItem(),可能需要异步服务器调用: https://github.com/facebook/flux/issues/60
管理客户端数据量: https://github.com/facebook/flux/issues/62
基本上,获取商店数据是同步的,然后组件可以告诉商店执行长时间运行的任务,然后忘记它。
在商店中完成任务后,会创建一个操作,并且流程就会发生,此时组件可以同步从商店获取所需的信息。
这有意义吗?
答案 2 :(得分:1)
如果我打算以尽可能多的Flux方式创建一个异步进程,我会像XHR请求那样处理它 - 在Action Creator或Store中启动异步进程(以最适合的方式为准)应用程序)然后调用新的Action Creator以在异步过程完成时调度新操作。这样,多个商店就可以响应已完成的昂贵的异步流程,而数据流仍然来自一个Action。
答案 3 :(得分:0)
您还可以向商店添加处理程序,以便在商店中发出特定事件时调用该处理程序
所以我们在商店里说你有一个方法:
Store = { ...
addUnreadDoneListener:function(callback){
this.on(SOME_EVENT_CONSTANT, callback);
},
...}
在你的componentWillMount中,你可以注册到这个&#34; addUnreadDoneListener&#34;具有组件功能,每次商店发出此特定事件时都会调用该组件。
我个人也在我的项目中这样做。我认为这很容易管理
希望这会有所帮助。
编辑:我忘记了...我使用Eventemitter来做这件事。