使用在React Components中转换DOM的JQuery插件?

时间:2014-08-21 21:43:12

标签: javascript jquery reactjs

有些JQuery插件不仅仅是为DOM节点添加行为,而是更改它们。例如,Bootstrap Switch转为

<input type="checkbox" name="my-checkbox" checked>

类似

<div class="bootstrap-switch bootstrap-switch-wrapper bootstrap-switch-on bootstrap-switch-large bootstrap-switch-animate">
  <div class="bootstrap-switch-container">
    <span class="bootstrap-switch-handle-on bootstrap-switch-primary">ON</span>
    <label class="bootstrap-switch-label">&nbsp;</label>
    <span class="bootstrap-switch-handle-off bootstrap-switch-default">OFF</span>
    <input type="checkbox" name="download-version" checked="" data-size="large" data-on-text="3" data-off-text="2.0.1">
  </div>
</div>

$("[name='my-checkbox']").bootstrapSwitch();

与React并不相符:

Uncaught Error: Invariant Violation: findComponentRoot(..., .0): Unable to find
element. This probably means the DOM was unexpectedly mutated (e.g., by the
browser), usually due to forgetting a <tbody> when using tables or nesting <p> or
<a> tags. ...<omitted>...`. 

是否有推荐的技术将这些插件合并到React组件中?或者它们是否从根本上打破了React的假设并且无法使用它?

4 个答案:

答案 0 :(得分:58)

不,反应会对任何在反应之外修改其自身组件dom结构的事情做出反应(哈哈)。这是你不想做的事情。建议的解决方案是复制您尝试使用jquery或类似插件进行反应的功能。

话虽如此,对于特定情况,有一种合理的方法可以做到这一点,你不能没有它,但它本质上意味着包装一些无反应的dom内部反应。

示例:

var Example = React.createClass({
    componentDidMount: function() {
        var $checkboxContainer = $(this.refs.checkboxContainer.getDOMNode());

        var $checkbox = $('<input />').prop('type', 'checkbox');

        $checkboxContainer.append($checkbox);

        $checkbox.bootstrapSwitch({});
    },
    render: function() {
        return (
            <div>
                <div ref="checkboxContainer"></div>
            </div>
        )
    }
});

现在,您正在使用嵌套div渲染组件。嵌套div第一次安装到dom时,嵌套div将获得一个jquery附加的复选框,然后它将在其上执行我们的jquery插件。

这个特定的示例组件没有什么意义,但是你可以看到它如何集成到一个更复杂的组件中,同时仍然允许你重新渲染和响应状态变化等。你只是失去了直接对...做出反应的能力事件/修改问题中的内容,就反应而言,不存在。

现在有了上面的例子,如果你有一些反应逻辑来添加/删除嵌套div,你必须在插入的div周围有相同的逻辑负责重新插入复选框并重新初始化它与jquery插件。但是因为react只在需要时修改了dom,所以这个插入的dom内容不会被删除,除非你做了一些修改容器div的方法,导致它被删除/重新渲染到dom。这意味着您仍然可以访问该容器div等的响应中的所有事件。

您还可以使用componentDidMount函数进行响应,将事件或回调绑定到复选框本身的特定交互。只需确保在componentWillUnmount中正确解除绑定,或者在特定情况下在组件生命周期中将其取消绑定。

答案 1 :(得分:21)

在这个精彩的ryanflorence教程中,您将了解如何执行此操作:

  

Wrapping DOM Libs

     

方法

     
      
  1. DOM libs通常操纵DOM
  2.   
  3. React尝试重新渲染并查找   一个与上次不同的DOM并且吓坏了
  4.   
  5. 我们隐藏了DOM   通过打破渲染树然后从React进行操作   重新连接lib操作的DOM。
  6.   
  7. 我们的消费者   组件可以留在React-land。
  8.   

答案 2 :(得分:8)

当然,有这样一种技术。我们一直在做这些事情。

  1. 您创建React组件来包装jQuery插件。
  2. render()内,您返回空<div ref="placeholder" />
  3. 在componentDidMount方法中,您通过其ref检索此元素,并在那里初始化您的jQuery插件。
  4. componentWillUnmount中,您要清理它。调用&#39; destroy&#39;或其他任何需要避免内存泄漏的内容。
  5. 那就是它。幸运的是,在React中以这种方式修改DOM是完全安全的。

    如果你想让这个插件对道具变化作出反应,事情会变得有点棘手。您需要覆盖其他生命周期方法,例如componentWillReceiveProps,检查道具实际更改的时间,并调用相应的插件方法。我可以更详细地解释一下,如果你有具体的问题,整个主题对于评论来说过于宽泛。

答案 3 :(得分:2)

这更像是一个哲学问题

创建React是为了优化DOM操作,并且当组件的状态通过setState更改时,在幕后有大量的连接方法

这样做会导致所述布线遍历其虚拟DOM以找到需要更新的节点

如果你必须使用React,是否试图保持编码的一致性,最好的办法就是在componentDidMount中应用JQuery DOM操作,如此...

    componentDidMount(){
        this.node = $("#"+this.props.id); // Keep a reference to the node
        this.chart = this.node.easyPieChart(); // Apply JQuery transformation and keep a reference
        this.percentTitle = this.chart.find(".percent"); // Keep a reference to the title
    }

这样做之后,无论你的“刷新”方法是什么,都不要调用setState,而是调用你的JQuery组件可能有的任何更新方法,如此...

        componentWillMount(){
            this.interval = setInterval(this._doRefresh.bind(this), 1500);
        }

        _doRefresh( percentage ){
            // Note how setState is NOT being called here
            percentage = percentage || Math.floor (Math.random() * 100) // simulate since we're not getting it yet
            this.chart.data('easyPieChart').update(percentage); // call easyPieChart's update
            this.percentTitle.text(percentage);
        }

此时,如果你问为什么要使用React,在我的情况下,这个组件是其他React组件列表中的一个项目,用于保持整个应用程序的一致性......你可以有类似的困境

如果,与我不同,你不幸的是你的组件没有更新方法,而且你不能制作一个,那么可能是重新考虑这种方法的时候了