我试图了解一些有点“神奇”的行为的根本原因,我发现我无法完全解释,并且从阅读 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
方法,打印出value
实际 DOM 节点的当前值。
在“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 示例