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

IT技术 javascript jquery reactjs
2021-02-22 00:40:13

一些 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个回答

不,react 会对在 react 之外修改其自身组件 dom 结构的任何内容做出严重react(哈哈)。这是你永远不想做的事情。推荐的解决方案是在react中复制您尝试使用 jquery 或类似插件执行的任何操作的功能。

话虽如此,对于特定的情况,有一种合理的方法可以在没有它的情况下做到这一点,但它本质上意味着将一些非react的 dom 包装在 react 中。

例子:

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 的组件。嵌套在第一次挂载到 dom 时,嵌套 div 将获得一个由 jquery 附加到它的复选框,然后它也会在其上执行我们的 jquery 插件。

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

现在在上面的例子中,如果你有一些react逻辑来添加/删除嵌套的 div,你必须在插入的 div 周围具有相同的逻辑,负责重新插入复选框并使用jQuery 插件。但是,因为 react 仅在需要时修改 dom,所以插入的 dom 内容不会被删除,除非您以某种方式修改容器 div,导致其被删除/重新渲染到 dom。这意味着您仍然可以访问该容器 div 等的 react 中的所有事件。

您还可以使用该componentDidMount函数作为react将事件或回调绑定到复选框本身的特定交互。只需确保componentWillUnmount在您的特定情况下在组件生命周期中正确地或在任何有意义的地方解除它们的绑定

如果父级重新渲染,子级将重新渲染,从而消除 jQuery DOM 操作。也许使用componentDidUpdate代替或以及componentDidMount
2021-04-17 00:40:13
感谢您的非常深入的讨论!
2021-04-28 00:40:13
只是对利用 React 生命周期方法来做 DOM 的东西的这个概念的补充,我已经使用 shouldComponentUpdate 和 componentDidMount 来处理 D3 等的动画内容:stackoverflow.com/questions/21903604/…… 通常我找到了最好的如果你正在做任何非反应性的事情,那么使用 React 的方法是确保它以某种方式与生命周期方法相关联。
2021-05-06 00:40:13
很好的答案!在过去的几个月里,我非常深入地研究 React,我认为你的“复制功能”建议是正确的。令人难以置信的是,React 已经淘汰了多少 jQuery 插件,因为实现相同的功能通常比 jQuery 插件要配置的代码更短、更简单。例如,如果我想显示一个 'XXX characters left' 类型的字段,在我的 render() 中放入状态是微不足道的:<div>{this.props.maxChars - this.state.q.length} characters left</div>
2021-05-09 00:40:13
@DanRoss 你确定吗?我认为 react 只会对 DOM 进行细粒度的更改,它并没有真正替换它的元素批发。
2021-05-13 00:40:13

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

包装 DOM 库

方法

  1. DOM 库通常操作 DOM
  2. React 尝试重新渲染并找到与上次不同的 DOM 并吓坏了
  3. 我们通过破坏渲染树然后在 lib 操作的 DOM 周围重新连接来对 React 隐藏 DOM 操作。
  4. 我们组件的使用者可以留在 React-land。

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

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

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

如果您希望此插件对props更改做出react,事情会变得更加棘手。您需要覆盖其他生命周期方法,例如componentWillReceiveProps,在 props 实际更改时检查,并调用相应的插件方法。我可以更详细地解释,如果您有具体问题,整体主题太宽泛无法评论。

这更像是一个哲学问题

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 组件列表中的一个项目,用于保持整个应用程序的一致性......你可能有类似的困境

如果与我不同,您很不幸,您的组件没有更新方法,并且您无法创建更新方法,那么可能是时候完全重新考虑该方法了