web-dev-qa-db-fra.com

Utiliser des plugins JQuery qui transforment le DOM en React Components?

Certains plugins JQuery n'ajoutent pas seulement un comportement aux nœuds DOM, mais les changent. Par exemple, Bootstrap Switch tours

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

en quelque chose comme

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

avec

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

Qui ne plonge pas avec 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>...`. 

Existe-t-il une technique recommandée pour incorporer ces plugins dans les composants React? Ou est-ce qu'ils cassent fondamentalement les hypothèses de React et ne peuvent pas fonctionner avec cela?)

47
tnunamak

Non, react réagira mal (haha) à tout ce qui modifie sa propre structure dom de composants en dehors de react. C'est quelque chose que vous ne voulez jamais faire. La solution recommandée serait de répliquer la fonctionnalité de tout ce que vous essayez de faire avec un plugin jquery ou similaire, en réaction.

Cela dit, il existe un moyen raisonnable de le faire pour des cas spécifiques où vous ne pouvez pas vous en passer, mais cela signifie essentiellement encapsuler un dom non réactif à l'intérieur de React.

Exemple:

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>
        )
    }
});

Maintenant, bien sûr, vous rendez un composant avec un div imbriqué. Le niché lorsqu'il est monté sur le dom pour la première fois que le div imbriqué recevra une case à cocher ajoutée par jquery, qui exécutera également notre plug-in jquery dessus.

Cet exemple de composant particulier a peu d'intérêt, mais vous pouvez voir comment cela pourrait s'intégrer dans un composant plus complexe tout en vous permettant de restituer et de réagir aux changements d'état, etc. Vous perdez simplement la capacité de réagir directement aux événements/modifier des choses à l'intérieur de la case à cocher en question qui, en ce qui concerne réagir, n'existe pas.

Maintenant, avec l'exemple ci-dessus, si vous deviez avoir une logique de réaction pour ajouter/supprimer le div imbriqué, vous devriez avoir la même logique autour de ce div inséré pour être responsable de la réinsertion de la case à cocher et de la réinitialisation avec le plugin jquery. Cependant, parce que react ne modifie le dom qu'en cas de besoin, ce contenu dom inséré ne sera pas supprimé à moins que vous ne fassiez quelque chose qui modifie la div du conteneur de manière à le supprimer/restituer au dom. Cela signifie que vous pouvez toujours accéder à tous les événements dans react pour cette division de conteneur, etc.

Vous pouvez également utiliser la fonction componentDidMount pour réagir pour lier des événements ou des rappels à des interactions spécifiques sur la case à cocher elle-même. Assurez-vous simplement de les dissocier correctement dans componentWillUnmount ou partout où cela est logique de le faire dans le cycle de vie des composants dans votre cas spécifique.

58
Mike Driver

Dans ce super tutoriel de ryanflorence, vous aurez une idée de la façon de procéder:

Wrapping DOM Libs

Méthodologie

  1. Les bibliothèques DOM manipulent généralement le DOM
  2. React essaie de restituer et trouve un DOM différent de celui qu'il avait la dernière fois et panique
  3. Nous masquons la manipulation DOM de React en cassant l'arborescence de rendu puis en vous reconnectant autour du DOM manipulé par la bibliothèque.
  4. Les consommateurs de notre composante peuvent rester dans React-land.
21
mik01aj

Bien sûr, il existe une telle technique. Nous faisons ces choses tout le temps.

  1. Vous créez React composant pour envelopper le plugin jQuery.
  2. À l'intérieur de votre render(), vous retournez un <div ref="placeholder" /> Vide
  3. Dans votre méthode componentDidMount, vous récupérez cet élément par sa référence et y initialisez votre plugin jQuery.
  4. Dans votre componentWillUnmount, vous le nettoyez. Appelant "détruire", ou toute autre chose requise pour éviter les fuites de mémoire.

C'est ça. Heureusement, il est complètement sûr de modifier DOM de cette manière dans React.

Si vous voulez que ce plugin réagisse aux changements d'accessoires, les choses deviennent un peu plus compliquées. Vous devez remplacer les autres méthodes de cycle de vie, comme componentWillReceiveProps, vérifier chaque fois que les accessoires changent réellement et appeler les méthodes de plug-in correspondantes. Je peux expliquer plus en détail, si vous avez des questions spécifiques, le sujet global est trop large pour le commentaire.

8
gaperton

C'est plus une question philosophique

React a été créé pour optimiser les manipulations DOM et a beaucoup de câblage en arrière-plan pour le faire lorsque l'état d'un composant change via setState

Cela entraînera ledit câblage à traverser son DOM virtuel pour trouver les nœuds qui doivent être mis à jour

Si vous devez utiliser React, que ce soit pour essayer de garder un niveau de cohérence dans votre codage, votre meilleur pari est d'appliquer la manipulation JQuery DOM à l'intérieur du componentDidMount comme ça ...

    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
    }

Cela fait, quelle que soit votre méthode de "rafraîchissement", ne faites PAS d'appels à setState, appelez plutôt la méthode de mise à jour que votre composant JQuery peut avoir, comme ça ...

        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);
        }

À ce stade, si vous demandez pourquoi utiliser React du tout, eh bien, dans mon cas, ce composant est un élément dans une liste d'autres React composants et a été utilisé pour maintenir la cohérence tout au long de l'application ... Vous pouvez avoir un dilemme similaire

Si, contrairement à moi, vous avez la malchance que votre composant ne dispose pas d'une méthode de mise à jour et que vous ne puissiez pas en créer une, il est peut-être temps de repenser complètement l'approche

2
David Glass