【发布时间】:2015-05-09 10:40:10
【问题描述】:
我试图了解一些我无法完全解释的有点“神奇”的行为的根本原因,这在阅读 ReactJS 源代码时并不明显。
当同步调用setState 方法以响应输入上的onChange 事件时,一切都按预期工作。输入的“新”值已经存在,因此 DOM 实际上并未更新。这是非常可取的,因为这意味着光标不会跳到输入框的末尾。
然而,当运行一个结构完全相同但调用setState异步的组件时,输入的“新”值似乎不存在,导致 ReactJS 实际触及DOM,这会导致光标跳到输入的末尾。
显然,在异步情况下,有些东西正在干预以将输入“重置”回其先前的value,而在同步情况下却没有这样做。这是什么机制?
同步示例
var synchronouslyUpdatingComponent =
React.createFactory(React.createClass({
getInitialState: function () {
return {value: "Hello"};
},
changeHandler: function (e) {
this.setState({value: e.target.value});
},
render: function () {
var valueToSet = this.state.value;
console.log("Rendering...");
console.log("Setting value:" + valueToSet);
if(this.isMounted()) {
console.log("Current value:" + this.getDOMNode().value);
}
return React.DOM.input({value: valueToSet,
onChange: this.changeHandler});
}
}));
请注意,代码将登录render方法,打印出实际DOM节点的当前value。
当在“Hello”的两个 L 之间输入“X”时,我们会看到以下控制台输出,并且光标停留在预期的位置:
Rendering...
Setting value:HelXlo
Current value:HelXlo
异步示例
var asynchronouslyUpdatingComponent =
React.createFactory(React.createClass({
getInitialState: function () {
return {value: "Hello"};
},
changeHandler: function (e) {
var component = this;
var value = e.target.value;
window.setTimeout(function() {
component.setState({value: value});
});
},
render: function () {
var valueToSet = this.state.value;
console.log("Rendering...");
console.log("Setting value:" + valueToSet);
if(this.isMounted()) {
console.log("Current value:" + this.getDOMNode().value);
}
return React.DOM.input({value: valueToSet,
onChange: this.changeHandler});
}
}));
这与上面完全相同,只是对setState 的调用是在setTimeout 回调中。
在这种情况下,在两个 L 之间键入 X 会产生以下控制台输出,并且光标会跳到输入的末尾:
Rendering...
Setting value:HelXlo
Current value:Hello
这是为什么?
我理解 React 的 Controlled Component 概念,因此忽略用户对 value 的更改是有道理的。但看起来value 实际上已更改,然后显式重置。
显然,同步调用setState确保它在重置之前生效,而在任何其他时间调用setState发生在重置之后,强制重新-渲染。
这是真的吗?
JS Bin 示例
【问题讨论】:
-
你能把这些变成两个 jsfiddle/jsbin/etc 的例子吗?这听起来像是 React 中的一个错误,但有工作代码来修补会很有用
-
@mike-pomax-kamermans 完成,已添加链接。
标签: javascript reactjs