50 votes

Comment utiliser jQuery UI avec React JS

Comment utiliser jQuery UI avec React ? J'ai vu quelques exemples en cherchant sur Google, mais ils semblent tous être dépassés.

2 votes

Oui, je dois le faire. Pour un projet, je dois utiliser beaucoup de composants de jquery ui.

1 votes

Parce que je dois aussi gérer un grand nombre d'États ? @azium

94voto

Kaloyan Kosev Points 5556

Si vous realmente besoin de le faire, voici une approche que j'utilise.

Le plan : Créer un composant pour gérer le plugin jQuery . Ce composant fournira une vue centrée sur React du composant jQuery. De plus, il :

  • Utilisez les méthodes du cycle de vie de React pour initialiser et démonter le plugin jQuery ;
  • Utiliser React props comme options de configuration du plugin et s'accrocher aux événements des méthodes du plugin ;
  • Détruit le plugin lorsque le composant se démonte.

Voyons, à l'aide d'un exemple concret, comment procéder avec la fonction jQuery UI Sortable plugin.


TLDR : La version finale

Si vous souhaitez simplement récupérer la version finale de l'exemple de jQuery UI Sortable :

... de plus, ci-dessous se trouve le raccourci des commentaires plus longs extrait de code :

class Sortable extends React.Component {
    componentDidMount() {
        this.$node = $(this.refs.sortable);
        this.$node.sortable({
            opacity: this.props.opacity,
            change: (event, ui) => this.props.onChange(event, ui)
        });
    }

    shouldComponentUpdate() { return false; }

    componentWillReceiveProps(nextProps) {
        if (nextProps.enable !== this.props.enable)
            this.$node.sortable(nextProps.enable ? 'enable' : 'disable');
    }

    renderItems() {
        return this.props.data.map( (item, i) =>
            <li key={i} className="ui-state-default">
                <span className="ui-icon ui-icon-arrowthick-2-n-s"></span>
                { item }
            </li>
        );
    }
    render() {
        return (
            <ul ref="sortable">
                { this.renderItems() }
            </ul>
        );
    }

    componentWillUnmount() {
        this.$node.sortable('destroy');
    }
};

En option, vous pouvez définir des accessoires par défaut (dans le cas où aucun n'est passé) et les types d'accessoires :

Sortable.defaultProps = {
    opacity: 1,
    enable: true
};

Sortable.propTypes = {
    opacity: React.PropTypes.number,
    enable: React.PropTypes.bool,
    onChange: React.PropTypes.func.isRequired
};

... et voici comment utiliser l'option <Sortable /> composant :

class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        // Use this flag to disable/enable the <Sortable />
        this.state = { isEnabled: true };

        this.toggleEnableability = this.toggleEnableability.bind(this);
    }

    toggleEnableability() {
        this.setState({ isEnabled: ! this.state.isEnabled });
    }

    handleOnChange(event, ui) {
        console.log('DOM changed!', event, ui);
    }

    render() {
        const list = ['ReactJS', 'JSX', 'JavaScript', 'jQuery', 'jQuery UI'];

        return (
            <div>
                <button type="button"
                    onClick={this.toggleEnableability}>
                    Toggle enable/disable
                </button>
                <Sortable
                    opacity={0.8}
                    data={list}
                    enable={this.state.isEnabled}
                    onChange={this.handleOnChange} />
            </div>
        );
    }
}

ReactDOM.render(<MyComponent />, document.getElementById('app'));

L'explication complète

Pour ceux d'entre vous qui veulent comprendre por qué y cómo . Voici un guide étape par étape :

Étape 1 : créer un composant.

Notre composant acceptera un tableau (liste) d'éléments (chaînes de caractères) en tant que data prop.

class Sortable extends React.Component {
    componentDidMount() {
        // Every React component has a function that exposes the
        // underlying DOM node that it is wrapping. We can use that
        // DOM node, pass it to jQuery and initialize the plugin.

        // You'll find that many jQuery plugins follow this same pattern
        // and you'll be able to pass the component DOM node to jQuery
        // and call the plugin function.

        // Get the DOM node and store the jQuery element reference
        this.$node = $(this.refs.sortable);

        // Initialize the jQuery UI functionality you need
        // in this case, the Sortable: https://jqueryui.com/sortable/
        this.$node.sortable();
    }

    // jQuery UI sortable expects a <ul> list with <li>s.
    renderItems() {
        return this.props.data.map( (item, i) =>
            <li key={i} className="ui-state-default">
                <span className="ui-icon ui-icon-arrowthick-2-n-s"></span>
                { item }
            </li>
        );
    }
    render() {
        return (
            <ul ref="sortable">
                { this.renderItems() }
            </ul>
        );
    }
};

Étape 2 : Passer les options de configuration via les props

Disons que nous voulons configurer l'opacité de l'aide pendant le tri . Nous utiliserons le opacity dans la configuration du plugin, qui prend les valeurs de 0.01 a 1 .

class Sortable extends React.Component {
    // ... omitted for brevity

    componentDidMount() {
        this.$node = $(this.refs.sortable);

        this.$node.sortable({
            // Get the incoming `opacity` prop and use it in the plugin configuration
            opacity: this.props.opacity,
        });
    }

    // ... omitted for brevity
};

// Optional: set the default props, in case none are passed
Sortable.defaultProps = {
    opacity: 1
};

Et voici comment nous pouvons utiliser le composant dans notre code maintenant :

<Sortable opacity={0.8} />

De la même manière, nous pouvons mettre en correspondance n'importe quelle jQUery UI Sortable options .

Étape 3 : Accrocher les fonctions sur les événements du plugin.

Vous aurez très probablement besoin de vous connecter à certaines des méthodes du plugin, afin d'exécuter une certaine logique React, par exemple, manipuler l'état let's day.

Voici comment faire :

class Sortable extends React.Component {
    // ... omitted for brevity

    componentDidMount() {
        this.$node = $(this.refs.sortable);

        this.$node.sortable({
            opacity: this.props.opacity,
            // Get the incoming onChange function
            // and invoke it on the Sortable `change` event
            change: (event, ui) => this.props.onChange(event, ui)
        });
    }

    // ... omitted for brevity
};

// Optional: set the prop types
Sortable.propTypes = {
    onChange: React.PropTypes.func.isRequired
};

Et voici comment l'utiliser :

<Sortable
    opacity={0.8}
    onChange={ (event, ui) => console.log('DOM changed!', event, ui) } />

Étape 4 : Passer le contrôle des futures mises à jour à jQuery

Juste après que ReactJS ajoute l'élément dans le DOM actuel, nous devons passer le contrôle futur à jQuery. Sinon, ReactJS ne rendra jamais à nouveau notre composant, mais ce n'est pas ce que nous voulons. Nous voulons que jQuery soit responsable de toutes les mises à jour.

Les méthodes du cycle de vie de React viennent à la rescousse !

Utilisez shouldComponentUpdate() pour faire savoir à React si la sortie d'un composant n'est pas affectée par le changement actuel d'état ou de props. Le comportement par défaut est de refaire le rendu à chaque changement d'état, et dans la grande majorité, mais nous ne voulons pas ce comportement !

shouldComponentUpdate() est invoqué avant le rendu lorsque de nouveaux accessoires ou états sont reçus. Si shouldComponentUpdate() renvoie à false entonces componentWillUpdate() , render() y componentDidUpdate() ne sera pas invoquée.

Ensuite, nous utilisons componentWillReceiveProps() nous comparons this.props con nextProps et appeler les mises à jour de jQuery UI Sortable uniquement lorsque cela est nécessaire. Pour cet exemple, nous allons implémenter l'option d'activation/désactivation du jQuery UI Sortable.

class Sortable extends React.Component {
    // Force a single-render of the component,
    // by returning false from shouldComponentUpdate ReactJS lifecycle hook.
    // Right after ReactJS adds the element in the actual DOM,
    // we need to pass the future control to jQuery.
    // This way, ReactJS will never re-render our component,
    // and jQuery will be responsible for all updates.
    shouldComponentUpdate() {
        return false;
    }

    componentWillReceiveProps(nextProps) {
        // Each time when component receives new props,
        // we should trigger refresh or perform anything else we need.
        // For this example, we'll update only the enable/disable option,
        // as soon as we receive a different value for this.props.enable
        if (nextProps.enable !== this.props.enable) {
            this.$node.sortable(nextProps.enable ? 'enable' : 'disable');
        }
    }

    // ... omitted for brevity
};

// Optional: set the default props, in case none are passed
Sortable.defaultProps = {
    enable: true
};

// Optional: set the prop types
Sortable.propTypes = {
    enable: React.PropTypes.bool
};

Étape 5 : Nettoyez le désordre.

De nombreux plugins jQuery fournissent un mécanisme pour faire le ménage lorsqu'ils ne sont plus nécessaires. jQuery UI Sortable fournit un événement que nous pouvons déclencher pour dire au plugin de délier ses événements DOM et de se détruire. Les méthodes de cycle de vie de React viennent à nouveau à la rescousse et fournissent un mécanisme à utiliser lorsque le composant est démonté.

class Sortable extends React.Component {
    // ... omitted for brevity

    componentWillUnmount() {
        // Clean up the mess when the component unmounts
        this.$node.sortable('destroy');
    }

    // ... omitted for brevity
};

Conclusion

Envelopper les plugins jQuery avec React n'est pas toujours le meilleur choix. Cependant, il est bon de savoir que c'est une option et comment vous pouvez mettre en œuvre une solution. C'est une option viable si vous migrez une application jQuery héritée vers React ou peut-être que vous ne pouvez tout simplement pas trouver un plugin React qui convient à vos besoins dans votre cas.

Dans le cas où une bibliothèque modifie le DOM, nous essayons de garder React en dehors de son chemin. . React fonctionne mieux lorsqu'il a le contrôle total du DOM. Dans ces cas, les composants React sont plus des enveloppes pour les bibliothèques tierces. La plupart du temps en utilisant le componentDidMount/componentWillUnmount pour initialiser/détruire la bibliothèque tierce. Et les props comme un moyen de donner au parent un moyen de personnaliser le comportement de la bibliothèque tierce que l'enfant enveloppe et de se connecter aux événements du plugin.

Vous pouvez utiliser cette approche pour intégrer presque tous les plugins jQuery. !

0 votes

Savez-vous comment utiliser un composant qui manipule le DOM. Comme Jquery UI sortable et d'autres bibliothèques qui manipulent le DOM.

1 votes

@Luke101 J'ai modifié ma réponse et j'utilise maintenant jQuery UI Sortable comme exemple. Dans le cas où une bibliothèque modifie le DOM, comme le fait le Sortable, nous essayons de garder React en dehors de son chemin. React fonctionne mieux lorsqu'il a le contrôle total du DOM. Dans ces cas, les composants React sont plus des enveloppes pour les bibliothèques tierces (comme je l'ai montré). J'ai aussi fait un jsfiddle avec la démo : jsfiddle.net/superKalo/x7dxbrw4

0 votes

Il manque quelque chose dans vos composants. Vous ne nous montrez pas du tout où vous importez le plugin jQuery.

11voto

John Points 2533

React ne joue pas bien avec les bibliothèques qui font des mutations directes du DOM. Si quelque chose d'autre modifie le DOM à l'endroit où React tente d'effectuer le rendu, il lancera des erreurs. Si vous avait Pour que cela fonctionne, le meilleur compromis est d'avoir différentes parties de votre page qui sont gérées par des choses différentes, par exemple une div qui abrite votre ou vos composants jquery, puis une autre div qui contient votre ou vos composants React. La communication entre ces composants disparates (jquery et react) sera toutefois difficile et, honnêtement, il est probablement préférable de choisir l'un ou l'autre.

8voto

ChillyPenguin Points 131

Bien que techniquement irréprochable, la réponse de Kayolan a un défaut fatal, IMHO : en passant la responsabilité des futures mises à jour de l'interface utilisateur de React à jQuery, il a plutôt annulé l'intérêt de la présence de React en premier lieu ! React contrôle le rendu initial de la liste triable, mais après cela, les données d'état de React seront périmées dès que l'utilisateur effectuera les premières opérations de glisser/trier de jQueryUI. Et tout l'intérêt de React est de représenter vos données d'état au niveau de la vue.

J'ai donc adopté la démarche inverse lorsque j'ai abordé ce problème : j'ai essayé de faire en sorte que React ait le contrôle autant que possible. Je ne laisse pas le contrôle jQueryUI Sortable changer le DOM du tout .

Comment est-ce possible ? Eh bien, la méthode sortable() de jQuery-ui possède une fonction cancel qui remet l'interface utilisateur dans l'état où elle était avant que vous ne commenciez à glisser et déposer des objets. L'astuce consiste à lire l'état du contrôle triable avant vous émettez que cancel appel. De cette façon, nous pouvons récupérer ce que l'utilisateur intentions étaient, avant le cancel L'appel remet le DOM comme il était avant. Une fois que nous avons ces intentions, nous pouvons les passer à React, et manipuler les données d'état pour qu'elles soient dans le nouvel ordre que l'utilisateur voulait. Enfin, appelez un setState() sur ces données pour que React rende le nouvel ordre.

Voici comment je fais :

  1. Attachez la méthode jquery-ui.sortable() à une liste d'articles de ligne (générée par React bien sûr !)
  2. Laissez l'utilisateur glisser et déposer ces éléments de ligne dans le DOM.
  3. Lorsque l'utilisateur commence à glisser, nous lisons l'index de l'élément de ligne que l'utilisateur fait glisser. de .
  4. Lorsque l'utilisateur dépose le poste, nous :
    1. Lire à partir de jQuery-ui.sortable() la nouvelle position d'index pour l'élément de ligne, c'est-à-dire où dans la liste l'utilisateur abandonné il.
    2. Passez un cancel appel à jQuery-ui.sortable() pour que la liste revienne à sa position originale, et que le DOM reste inchangé.
    3. Transmettez les anciens et nouveaux index de l'élément de ligne glissé comme paramètres à une fonction JavaScript dans un module React.
    4. Demandez à cette fonction de réorganiser les données d'état de la liste pour qu'elles soient dans le nouvel ordre dans lequel l'utilisateur les a glissées et déposées.
    5. Faire un React setState() appeler.

La liste dans l'interface utilisateur reflétera maintenant le nouvel ordre de nos données d'état ; c'est une fonctionnalité standard de React.

Ainsi, nous pouvons utiliser la fonctionnalité de glisser-déposer de jQueryUI Sortable, mais sans modifier le DOM du tout. React est heureux, parce qu'il a le contrôle du DOM (là où il devrait être).

Exemple de dépôt Github à https://github.com/brownieboy/react-dragdrop-test-simple . Il comprend un lien vers une démonstration en direct.

2 votes

J'ai aimé les deux réponses. Votre réponse est la façon dont j'ai implémenté l'intégration entre Knockout.js et jQuery UI sortable il y a des années, ce qui rend heureux à la fois KO et les implémentations de DOM virtuel comme React. D'un autre côté, il est bon de savoir que nous avons un mécanisme de repli lorsque nous ne pouvons pas annuler les modifications du DOM par des bibliothèques tierces.

0 votes

C'est une très bonne solution, qui oblige à penser de manière réactive plutôt que de penser de manière traditionnelle. Je dois admettre que je suis arrivé aux mêmes conclusions que Kaloyan et que j'utilise une technique similaire depuis. Laisser react contrôler le rendu implique un travail manuel pour chaque composant différent, mais donnera les meilleurs résultats. Envelopper simplement les composants a le potentiel d'introduire des bugs subtils (ce dont je témoigne). Cette réponse devrait obtenir beaucoup plus de votes.

0 votes

Avec le même concept que cette réponse. Certains composants simples (par exemple un calendrier) peuvent synchroniser leur état avec Reace/Vue/Angular en utilisant les fonctions props/événements intégrées. Exemple : vuejsdevelopers.com/2017/05/20/vue-js-safely-jquery-plugin

5voto

SamuraiCharlie Points 116

Je n'ai pas réussi à faire fonctionner le paquet npm jquery-ui. Ce qui a fonctionné pour moi est d'utiliser jquery-ui-bundle :

import $ from 'jquery';
import 'jquery-ui-bundle';
import 'jquery-ui-bundle/jquery-ui.min.css';

0 votes

Mec, après tant de tentatives, c'est celle qui a fonctionné pour moi. Merci !

0 votes

Je pense que cette réponse doit être acceptée.

1voto

Fabian Picone Points 991

En ce qui concerne La longue réponse de Kaloyan Kosev Je dois créer un composant pour chaque fonctionnalité de jQueryUi que je veux utiliser ? Non, merci ! Pourquoi ne pas simplement mettre à jour votre état lorsque vous modifiez le DOM ? Ce qui suit fonctionne pour moi :

export default class Editor extends React.Component {

    // ... constructor etc.

    componentDidMount() {
        this.initializeSortable();
    }

    initializeSortable() {
        const that = this;
        $('ul.sortable').sortable({
            stop: function (event, ui) {
                const usedListItem = ui.item;
                const list = usedListItem.parent().children();
                const orderedIds = [];
                $.each(list, function () {
                    orderedIds.push($(this).attr('id'));
                })
                that.orderSortableListsInState(orderedIds);
            }
        });
    }

    orderSortableListsInState(orderedIds) {

        // ... here you can sort the state of any list in your state tree

        const orderedDetachedAttributes = this.orderListByIds(orderedIds, this.state.detachedAttributes);
        if (orderedDetachedAttributes.length) {
            this.state.detachedAttributes = orderedDetachedAttributes;
        }
        this.setState(this.state);
    }

    orderListByIds(ids, list) {
        let orderedList = [];
        for (let i = 0; i < ids.length; i++) {
            let item = this.getItemById(ids[i], list);
            if (typeof item === 'undefined') {
                continue;
            }
            orderedList.push(item);
        }
        return orderedList;
    }

    getItemById(id, items) {
        return items.find(item => (item.id === id));
    }

    // ... render etc.

}

L'élément liste a juste besoin d'un attribut supplémentaire pour permettre à jQuery de sélectionner l'élément.

import React from 'react';

export default class Attributes extends React.Component {
    render() {
        const attributes = this.props.attributes.map((attribute, i) => {
           return (<li key={attribute.id} id={attribute.id}>{attribute.name}</li>);
        });

        return (
            <ul className="sortable">
                {attributes}
            </ul>
        );
    }
}

Pour les identifiants, j'utilise UUID donc je n'ai pas eu de conflits en les faisant correspondre dans orderSortableListsInState() .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X