简体   繁体   English

在 KnockoutJS 中使用 React 组件

[英]Using React components inside KnockoutJS

We have a big web application mostly built using KnockoutJS.我们有一个主要使用 KnockoutJS 构建的大型 Web 应用程序。 I'm looking at if there is a possibility to migrate to React, but in a way that require rewriting the entire application.我正在研究是否有可能迁移到 React,但需要重写整个应用程序。 My idea was to use a bottom-up approach: start by replacing the basic building blocks one by one.我的想法是使用自下而上的方法:从一个接一个替换基本构建块开始。

I was inspired by some prior work to call ReactDOM.render inside a KnockoutJS binding handler:我受到之前的一些工作的启发, 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 has its own dependency tracking system, so it will call the update method whenever some data changes. Knockout 有自己的依赖跟踪系统,所以只要数据发生变化,它就会调用 update 方法。

It works perfectly, and the component is re-rendered to reflect any changes to data.它工作得很好,组件被重新渲染以反映对数据的任何更改。 However, it breaks down when data is updated inside a React event handler.但是,当在 React 事件处理程序中更新数据时,它会崩溃。 Eg, this component does not work as expected:例如,此组件无法按预期工作:

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

Note: value in this case is a ko.observable, and calling it like in the event handler will cause the bindingHandler's update method to be called, which in turn calls ReactDOM.render.注意:这里的 value 是一个 ko.observable,在事件处理程序中调用它会导致调用 bindingHandler 的 update 方法,该方法又调用 ReactDOM.render。 However, this render only works once, after that the component stops updating.但是,此渲染仅工作一次,之后组件停止更新。

The issue is demonstrated in this CodePen .这个问题在这个 CodePen 中得到了证明 Click the box and try to type something.单击该框并尝试键入一些内容。 One update goes through, after than, the component's function stops being called.一次更新完成后,该组件的函数停止被调用。

Edit: I believe the issue is that the second call to ReactDOM.render (when the value is updated by the user in the onChange handler) is not synchronous.编辑:我认为问题在于对 ReactDOM.render 的第二次调用(当用户在 onChange 处理程序中更新值时)不是同步的。 This means that Knockout's dependency detection cannot work and any subsequent calls no longer call the update method of the binding handler.这意味着 Knockout 的依赖检测无法工作,任何后续调用都不再调用绑定处理程序的更新方法。

Can this be circumvented somehow?这可以以某种方式规避吗?

For me worked:对我来说工作:

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

if you refresh your observable it will work but unfortunetaly i don't know how.如果你刷新你的observable它会起作用,但不幸的是我不知道如何。

As I guessed, the issue seems to be that ReactDOM.render is asynchronous in some cases - in this case when called from an event handler in React.正如我所猜测的,问题似乎是 ReactDOM.render 在某些情况下是异步的——在这种情况下,当从 React 中的事件处理程序调用时。

This means that if you dereference any observables that you depend on in the update method itself, Knockout's dependency tracking mechanism works as expected.这意味着,如果您取消引用更新方法本身中依赖的任何 observable,Knockout 的依赖项跟踪机制将按预期工作。 This is why the modification Gawel1908 proposed makes it work - not because the value is "reset", but because props.value is dereferenced.这就是为什么 Gawel1908 提议的修改使它起作用的原因 - 不是因为值是“重置”,而是因为 props.value 被取消引用。

I decided to instead use a convention: always unwrap any such observables in the valueAccessor itself:我决定改为使用约定:始终在 valueAccessor 本身中解开任何此类可观察对象:

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

And don't unwrap it in the component:并且不要在组件中解开它:

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

Updated, working codepen.更新,工作代码笔。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM