tnunamak tnunamak - 3 months ago 30
React JSX Question

Using JQuery plugins that transform the DOM in React Components?

Some JQuery plugins don't just add behavior to DOM nodes, but change them. For example, Bootstrap Switch turns

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

into something like

<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">



Which doesn't jive with 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>...`.

Is there a recommended technique for incorporating these plugins into React components? Or do they fundamentally break the assumptions of React and cannot work with it?


No, react will react (haha) badly to anything that modifies its own component dom structure outside of react. This is something you don't ever want to do. The recommended solution would be to replicate the functionality of whatever you're trying to do with a jquery or similar plugin, in react.

Having said that, there is a reasonable way to do this for specific instances where you just can't do without it, but it essentially means wrapping some non-react dom inside react.


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

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


    render: function() {
        return (
                <div ref="checkboxContainer"></div>

Now of course you are rendering a component with a nested div. The nested when mounted to the dom for the first time that nested div will get a checkbox appended to it by jquery, which will then also execute our jquery plugin on it.

This particular example component has little point to it, however you can see how this might integrate into a more complex component while still allowing you to re-render and react to state changes etc. You just lose the ability to directly react to events/modify things inside of the checkbox in question which as far as react is concerned, doesn't exist.

Now with the above example if you were to have some react logic to add/remove the nested div, you'd have to have the same logic around that div being inserted be responsible for re-inserting the checkbox and re-initializing it with the jquery plugin. However because react only modifies the dom when needed, this inserted dom content wont be removed unless you do something that modifies the container div in a way that causes it to be removed/re-rendered to the dom. This means you can still access all of the events within react for that container div etc.

You could also make use of the componentDidMount function in react to bind events or callbacks to specific interactions on the checkbox itself. Just make sure to unbind them correctly in componentWillUnmount or wherever it makes sense to do so in the component lifecycle in your specific case.