【问题标题】:Using React components inside KnockoutJS在 KnockoutJS 中使用 React 组件
【发布时间】:2020-02-20 09:51:34
【问题描述】:

我们有一个主要使用 KnockoutJS 构建的大型 Web 应用程序。我正在研究是否有可能迁移到 React,但需要重写整个应用程序。我的想法是使用自下而上的方法:从一个个替换基本构建块开始。

我受到prior work 的启发,在 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,像在事件处理程序中一样调用它会导致 bindingHandler 的 update 方法被调用,进而调用 ReactDOM.render。但是,此渲染只工作一次,之后组件停止更新。

The issue is demonstrated in this CodePen。单击该框并尝试输入一些内容。一次更新完成后,组件的函数停止被调用。

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

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

【问题讨论】:

    标签: javascript reactjs knockout.js


    【解决方案1】:

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

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

    我决定改用一个约定:总是在 valueAccessor 本身中解开任何这样的 observables:

    <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)}/>;
    }
    

    Updated, working codepen.

    【讨论】:

    • 如何处理 React 组件的清理工作?我也使用相同的方法,当我移动到另一个页面时,我仍然看到在前一页中呈现的组件
    【解决方案2】:

    为我工作:

     update(element, valueAccessor) {
        const {component, props} = valueAccessor();
        props.value(props.value());
        ReactDOM.render(React.createElement(component, props), element);
    }
    

    如果你刷新你的observable 它会起作用,但不幸的是我不知道怎么做。

    【讨论】:

    • 我认为这证明了我已经相信的可能是错误 - 从事件处理程序调用时 ReactDOM.render 不是同步的。我也许可以根据这些信息制定解决方法。
    猜你喜欢
    • 2019-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    • 2017-04-16
    • 2018-01-17
    • 2018-04-07
    相关资源
    最近更新 更多