在 KnockoutJS 中使用 React 组件

wax*_*ing 5 javascript knockout.js reactjs

我们有一个大型 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);
    }
};
Run Code Online (Sandbox Code Playgroud)

Knockout 有自己的依赖跟踪系统,因此每当某些数据发生变化时它就会调用 update 方法。

它工作完美,并且组件被重新渲染以反映数据的任何更改。然而,当数据在 React 事件处理程序中更新时,它就会崩溃。例如,该组件无法按预期工作:

const Input = function ({value}) {
    return <input type="text" 
                  value={value()} 
                  onChange={e => value(e.target.value)}/>;
}
Run Code Online (Sandbox Code Playgroud)

注意:本例中的 value 是一个 ko.observable,像在事件处理程序中一样调用它会导致调用 bindingHandler 的 update 方法,进而调用 ReactDOM.render。但是,此渲染仅有效一次,之后组件将停止更新。

此 CodePen 中演示了该问题。单击该框并尝试输入一些内容。经过一次更新后,组件的函数将停止被调用。

编辑:我认为问题在于对 ReactDOM.render 的第二次调用(当用户在 onChange 处理程序中更新值时)不同步。这意味着 Knockout 的依赖项检测无法工作,并且任何后续调用都不再调用绑定处理程序的 update 方法。

这可以以某种方式规避吗?

wax*_*ing 4

正如我所猜测的,问题似乎是 ReactDOM.render 在某些情况下是异步的 - 在这种情况下,当从 React 中的事件处理程序调用时。

这意味着,如果您取消引用更新方法本身中依赖的任何可观察量,Knockout 的依赖项跟踪机制将按预期工作。这就是 Gawel1908 提出的修改使其起作用的原因 - 不是因为该值被“重置”,而是因为 props.value 被取消引用。

我决定改为使用约定:始终在 valueAccessor 本身中解开任何此类可观察量:

<div data-bind="react: { component: Input, props: { value: val(), setValue: val }}">
</div>
Run Code Online (Sandbox Code Playgroud)

并且不要将其解开到组件中:

const Input = function ({value, setValue}) {
  return <input 
           type="text" 
           value={value} 
           onChange={e => setValue(e.target.value)}/>;
}
Run Code Online (Sandbox Code Playgroud)

已更新,可工作的代码笔。