我们有一个大型的Web应用程序,主要使用KnockoutJS构建。我正在研究是否有可能迁移到React,但是这种方式需要重写整个应用程序。我的想法是使用一种自下而上的方法:首先将基本构建块一一替换。
我受到一些先前工作的启发,可以在KnockoutJS绑定处理程序中调用ReactDOM.render:
ko.bindingHandlers.react = {
init() {
return {controlsDescendantBindings: true};
},
update(element, valueAccessor) {
const {component, props} = valueAccessor();
ReactDOM.render(React.createElement(component, props), element);
}
};
Knockout有其自己的依赖项跟踪系统,因此每当某些数据更改时,它将调用update方法。
它运行完美,并且重新渲染了组件以反映对数据的任何更改。但是,当在React事件处理程序中更新数据时,它会崩溃。例如,此组件无法正常工作:
const Input = function ({value}) {
return <input type="text"
value={value()}
onChange={e => value(e.target.value)}/>;
}
注意:在这种情况下,value是一个ko.observable,并且像在事件处理程序中一样调用它会导致bindingBindingHandler的update方法被调用,而后者又会调用ReactDOM.render。但是,此渲染仅工作一次,之后组件停止更新。
此问题已在此CodePen中演示。单击该框,然后尝试键入一些内容。然后进行一次更新,然后停止调用组件的功能。
编辑:我相信问题是对ReactDOM.render的第二次调用(当用户在onChange处理程序中更新值时)不是同步的。这意味着淘汰赛的依赖关系检测无法正常工作,任何后续调用都不再调用绑定处理程序的update方法。
可以以某种方式规避吗?
如我所料,问题似乎是ReactDOM.render在某些情况下是异步的-在这种情况下,是从React中的事件处理程序调用时。
这意味着,如果您在更新方法本身中取消引用了您依赖的任何可观察对象,则Knockout的依赖项跟踪机制将按预期工作。这就是Gawel1908提出的修改使其起作用的原因-不是因为值被“重置”,而是因为props.value被取消引用了。
我决定改用约定:始终在valueAccessor本身中解开任何此类可观察对象:
<div data-bind="react: { component: Input, props: { value: val(), setValue: val }}">
</div>
并且不要在组件中拆开它:
const Input = function ({value, setValue}) {
return <input
type="text"
value={value}
onChange={e => setValue(e.target.value)}/>;
}