我在我的应用程序中使用flux,我使用Backbone.View
作为视图层。
通常,整个页面都有一个商店实例,商店保存应用程序的数据(或状态),视图将监听change
的{{1}}事件, store
触发store
事件,视图将相应地重新呈现。
到目前为止一直很好,但是当我使用表单时遇到一些问题,当使用尝试提交表单或为元素触发的change
事件时,我想验证服务器中的输入并显示错误尽快,这就是我所做的:
当用户点击提交按钮或为元素更改的值时,我将调度以下操作:
blur
dispatch({type:"validate",value:"value"});
将响应此操作并向服务器发送请求
当响应返回时,我将更新商店并触发store
事件:
change
我可以显示错误但是我无法保留元素的值,因为表单中的元素被重新渲染,这意味着它们将显示原始值而不是用户键入的值。
我认为在调度这样的验证操作时也会保存键入的值:
store.validate_response=response;
store.trigger("change");
当使用点击提交按钮时它起作用,因为一般当他们点击按钮时他们不会在表单中输入任何内容,但是这种情况怎么样:
dispatch({type:"validate",value:"value",userTypedValueForEveryElement:"....."});
<input type="text" id="A" />
<input type="text" id="B" />
avalue
中的用户类型input
,然后在A
bv
中输入input
,同时我会进行验证,并在发送动作时发送两个值:
B
{a:"avalue",b:"bv"}
将保留这些值。
在请求期间,用户继续键入元素store
,现在值为B
,同时返回验证响应,然后表单将重新呈现,它将为bvalue
设置avalue
,为A
设置bv
,这是重点,B
的值丢失,用户会感到惊讶,他们不会知道发生了什么。
有什么想法解决这个问题吗?
似乎是B
方式:
flux
使这种要求比之前复杂。一旦有很多交互式的视图,你就必须做更多额外的工作来保持视图的状态。
这是真的还是因为我错过了什么?
答案 0 :(得分:1)
听起来你在这里有一些不同的问题,但它们都是可以解决的。这有点长,但希望它能解决您遇到的所有问题。
商店设计:首先,您的商店实际上要保留哪些信息?尽量不要像使用Backbone模型那样考虑Flux商店,因为它们的用途并不完全相同。 Flux商店应存储应用程序状态的一部分(不一定是UI组件状态的一部分),并且不应该知道或关心使用它的任何视图。记住这一点可以帮助您将行为和数据放在正确的位置。因此,我们假设您的商店正在跟踪用户对特定表单的输入。由于您的应用程序关心输入是否有效,您需要以某种方式在商店中表示。您可以将每个输入表示为商店中的对象,例如{val: 'someInput', isValid: false}
。无论你存储它,它必须在那里;应用程序的任何部分都应该能够从商店中提取数据并知道哪些输入有效/无效。
我同意@korven认为在商店中放置大量应用程序逻辑是一个糟糕的选择。我将我的AJAX调用放入动作创建逻辑中,使用AJAX响应回调在Dispatcher上创建实际操作;我不止一次看过这个推荐。
保留用户输入:例如,您只想在用户输入完成后呈现表单输入 - 否则,渲染将在文本输入时更改文本。这很容易 - throttle或debounce(这里可能更适合去抖动)用户输入事件的输入验证处理程序。 (如果您正在使用焦点或模糊事件,则时间不太可能成为问题,但您仍应考虑它。)只有在验证完成后才能更新存储。当然,只有在商店更新时才会呈现。因此,当用户停止输入并且我们已经验证了他们的输入时,我们只修改DOM中的输入值。
即使使用限制/去抖动,由于验证请求是异步的,并且用户可能(可能)在短时间内触发许多验证请求,因此您无法依赖于按顺序返回的响应。换句话说,您无法在回复时处理每个回复;如果他们不按顺序回来,你将用旧输入覆盖最近的输入。 (我在现实生活中遇到过这种情况。对你来说这可能是一个优势,但是当它发生时,这个错误就会让人感到困惑,以至于值得提前解决。)幸运的是,我们只关心这个问题。 用户输入的最新内容。因此,除了最近请求的响应之外,我们可以忽略对验证请求的所有响应。您可以通过跟踪“键”来轻松地将此逻辑与提出请求的任何内容集成在一起。对于每个请求。以下是我如何解决这个问题的一个例子:
// something in your view
this.on(keyup, function() {
var input = this.getUserInput();
validationService.validate(input);
}
// within validationService
validate: function(input) {
// generate random int between 1 and 100
var randKey = Math.floor(Math.random() * (100 - 1)) + 1;
this.lastRequestKey = randKey;
this.doAjaxRequest({
data: {input: input},
callback: function() {
if (randKey !== this.lastRequestKey) {
// a newer request has modified this.lastRequestKey
return;
}
// do something to update the Store
});
}
在此示例中,负责验证服务的对象会记住&#39;只有最近设置的键#39;对于请求。由于其闭包,每个回调都在其范围内具有其原始键,并且它可以检查其原始键是否等于服务对象上设置的键。如果没有,这意味着发生了另一个请求,我们不再关心此响应。你会想要&#39;键&#39;要设置为每个字段,以便字段B的新请求不会覆盖字段A的旧请求。您可以通过其他方式解决此问题,但关键是,丢弃除最后一个请求之外的所有请求&#39;对任何给定输入的响应。这还有一个额外的好处,就是在那些丢弃的响应上保存一些更新/渲染周期。
多个字段呈现:当你正确地对待Flux时,你永远不应该失去&#39;数据,因为所有更改都来自Dispatcher / Store,并且因为Dispatcher不会向商店发送新更新,直到上一次更新完成。因此,只要您使用每个新输入更新商店,您就不会失去任何东西。您不必担心输入B的更改会导致您丢失对正在进行的输入A的更改,因为对输入A的更改将从Dispatcher流向Store到View 并且在Dispatcher允许更改输入B以开始处理之前完成渲染。 (这意味着渲染速度应该很快,因为他们会阻止下一次操作.React在Flux上运行良好的原因之一。)
只要你把所有东西都放到商店里 - 并且不要把错误的东西放到商店里,输入和异步处理上面的东西都会解决 - 你的用户界面会很准确。 Flux模式将每个更改视为原子事务,保证在下一次更改发生之前完成。
答案 1 :(得分:1)
在编写我的反应应用程序时,我遇到了完全相同的问题。结果我最终写了一个小型库来实现同样的目标。
https://www.npmjs.com/package/jsov
只要商店使用他们输入的数据触发更改,您需要做的就是这样做。您的组件中将有一个onChange函数,它将从商店中监听此更改(并且可能正在设置状态),现在您在此处执行的操作是在设置状态使用之前
onChange:function(){
var validated_response=JsOV.schemaValidator(Schema,Store.getResponse());
this.setState({data:validated_response});
}
P.S:为了省去痛苦,我还在库中提供了一个模式生成器功能。它需要一个虚拟响应并生成模式样板,您可以在其中添加自己的验证。