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

一些JQuery插件不只是添加行为到DOM节点,而是改变它们。 例如, Bootstrap开关打开

<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 (eg, by the browser), usually due to forgetting a <tbody> when using tables or nesting <p> or <a> tags. ...<omitted>...`. 

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

不,反应会对(哈哈)严重的反应,改变其自身组件的结构。 这是你永远不想做的事情。 推荐的解决scheme是复制你正在试图用jquery或类似的插件做的任何反应的function。

话虽如此,对于那些你不能没有它的具体实例来说,有一个合理的方法,但这实际上意味着把一些没有反应的东西包装在内部。

例:

 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的组件。 第一次将嵌套divembedded到dom中时,会得到一个由jquery附加的checkbox,然后它将执行我们的jquery插件。

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

现在用上面的例子,如果你有一些反应逻辑来添加/删除嵌套div,你必须有相同的逻辑围绕被插入的div负责重新插入checkbox,并重新初始化与jquery插件。 但是,因为只有在需要的时候才会修改dom,所以这个插入的dom内容不会被删除,除非你做了一些修改容器div的方法,使得它被移除/重新呈现给dom。 这意味着您仍然可以访问该容器的所有事件响应

您也可以使用componentDidMount函数来将事件或callback绑定到checkbox本身的特定交互。 只要确保在componentWillUnmount正确解除绑定即可,或者在特定情况下组件生命周期中的任意位置解除绑定。

在这个伟大的ryanflorence的教程,你会得到一个想法如何做到这一点:

包装DOM Libs

方法

  1. DOM库通常操纵DOM
  2. React尝试重新渲染,并find与上一次不同的DOM,并且吓坏了
  3. 我们通过中断渲染树来隐藏React中的DOM操作,然后重新连接这个lib操作的DOM。
  4. 我们组件的消费者可以留在React-land。

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

  1. 你创buildReact组件来包装jQuery插件。
  2. render()内部,你返回一个空的<div ref="placeholder" />
  3. 在你的componentDidMount方法中,你通过它的ref检索这个元素,并在那里初始化你的jQuery插件。
  4. 在你的componentWillUnmount ,你清理它。 调用'摧毁',或者其他任何需要避免内存泄漏的东西。

而已。 幸运的是,在React中以这种方式修改DOM是完全安全的。

如果你想让这个插件对道具变化做出反应,事情会变得更加棘手。 你需要重写其他的生命周期方法,比如componentWillReceiveProps ,只要道具实际改变了,就调用相应的插件方法。 我可以更详细地解释一下,如果您有具体的问题,整体的话题太广泛了。

这更像是一个哲学问题

React是为了优化DOM操作而创build的,并且在组件的状态通过setState更改时在幕后有很多布线

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

如果你必须使用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 } 

这样做,不pipe你的“刷新”方法是什么,不要调用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组件列表中的一个项目,并被用来保持整个应用程序的一致性…你可能有一个类似的困境

如果和我不一样,你不幸运的是你的组件没有更新方法,而你也不能创build更新方法,那么可能是时候重新思考这个方法了