Подтвердить что ты не робот

Как использовать jquery ui с React JS?

Я видел несколько примеров в google, но все они, похоже, устарели. Любая идея, как я могу использовать JQuery UI с ответом?

4b9b3361

Ответ 1

Если вам действительно нужно это сделать, вот подход, который я использую.

План: Создать компонент для управления плагином jQuery. Этот компонент предоставит ориентированное на React представление компонента jQuery. Более того, он будет:

  • Используйте методы жизненного цикла React для инициализации и срыва плагина jQuery;
  • Используйте React props в качестве параметров конфигурации плагина и подключайтесь к событиям методов плагинов;
  • Уничтожьте плагин при отключении компонентов.

Давайте рассмотрим практический пример, как это сделать с плагином jQuery UI Sortable.


TL;DR: окончательная версия

Если вы просто хотите получить окончательную версию обернутого примера экземпляра JQuery UI Sortable:

... плюс, ниже - сокращенный от фрагмента кода более длинных комментариев:

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

По желанию вы можете установить реквизиты по умолчанию (в случае отсутствия переданы) и типы поддержки:

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

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

... и здесь, как использовать компонент <Sortable />:

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

Полное объяснение

Для тех из вас, кто хочет понять почему и как. Вот пошаговое руководство:

Шаг 1. Создание компонента.

Наш компонент примет массив (список) элементов (строк) как 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>
        );
    }
};

Шаг 2: Передайте параметры конфигурации через реквизиты

Скажем, мы хотим настроить непрозрачность помощника при сортировке. Мы будем использовать параметр opacity в конфигурации плагина, который принимает значения от 0.01 до 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
};

И вот как мы можем теперь использовать компонент в нашем коде:

<Sortable opacity={0.8} />

Точно так же мы можем сопоставить любой параметр jQuery Sortable.

Шаг 3: Подключения к событиям плагина.

Вам, скорее всего, придется подключиться к некоторым из методов плагина, чтобы выполнить некоторую логику React, например, манипулировать состоянием let day.

Вот как это сделать:

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

А вот как его использовать:

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

Шаг 4: Передайте управление будущими обновлениями в jQuery

Сразу после того, как ReactJS добавит элемент в фактическую DOM, нам нужно передать будущий элемент управления jQuery. В противном случае ReactJS никогда не будет повторно отображать наш компонент, но мы этого не хотим. Мы хотим, чтобы jQuery отвечал за все обновления.

Способы жизненного цикла реагирования приходят на помощь!

Использовать shouldComponentUpdate(), чтобы позволить React знать, не влияет ли на компонентный вывод текущее изменение состояния или реквизита. Поведение по умолчанию заключается в повторном рендеринге при каждом изменении состояния и в подавляющем большинстве, но мы не хотим этого поведения!

shouldComponentUpdate() вызывается перед рендерингом при получении новых реквизитов или состояний. Если shouldComponentUpdate() возвращает false, то componentWillUpdate(), render() и componentDidUpdate() не будут вызываться.

Затем мы используем componentWillReceiveProps(), мы сравниваем this.props с nextProps и вызываем jQuery UI сортируемые обновления только при необходимости. В этом примере мы реализуем параметр enable/disable 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
};

Шаг 5: Очистите беспорядок.

Многие плагины jQuery предоставляют механизм для очистки после себя, когда они больше не нужны. jQuery UI Sortable предоставляет событие, которое мы можем вызвать, чтобы сообщить плагину развязать его события DOM и уничтожить. Реагирующие методы жизненного цикла снова оказываются на спасителе и обеспечивают механизм захвата, когда компонент размонтируется.

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

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

    // ... omitted for brevity
};

Заключение

Плагины плагинов jQuery с React не всегда являются лучшим выбором. Тем не менее, приятно знать, что это вариант и как вы можете реализовать решение. Это жизнеспособный вариант, если вы переносите устаревшее приложение jQuery на React или, возможно, просто не можете найти плагин React, который соответствует вашим потребностям в вашем случае.

В случае, если библиотека изменяет DOM, мы пытаемся сохранить React на своем пути. Реакция работает лучше всего, когда она полностью контролирует DOM. В этих случаях компоненты React являются более обертками для сторонних библиотек. В основном, используя компонентDidMount/componentWillUnmount для инициализации/уничтожения сторонней библиотеки. И реквизиты как способ дать родителям способ настройки поведения сторонней библиотеки, которую ребенок обертывает и подключать к событиям плагина.

Вы можете использовать этот подход для интеграции практически любого плагина jQuery!

Ответ 2

Реакция не очень хорошо работает с библиотеками, которые выполняют прямые DOM-мутации. Если что-то еще мутирует DOM, где React пытается выполнить рендеринг, он будет вызывать ошибки. Если вам нужно было выполнить эту работу, ваш лучший компромисс состоит в том, чтобы иметь разные части вашей страницы, которыми управляют разные вещи, например div, в котором размещаются ваши компоненты jquery, а затем какой-либо другой div, который содержит ваш компонент React ( с). Общение между этими разрозненными компонентами (jquery и реагировать) будет трудным, и, честно говоря, вероятно, лучше просто выбрать тот или другой.

Ответ 3

В то время как технически безупречный, ответ Кайолана имеет фатальный недостаток, ИМХО: передавая ответственность за будущие обновления пользовательского интерфейса от React to jQuery, он скорее отрицал, что точка Реагента была там в первую очередь! React контролирует первоначальный рендеринг сортируемого списка, но после этого данные состояния React будут устаревать, как только пользователь выполнит первые операции jQueryUI для перетаскивания/сортировки. И весь смысл React - представлять ваши данные состояния на уровне представления.

Итак, я применил обратный подход, когда я подошел к этой проблеме: я пытался обеспечить, чтобы React контролировал как можно больше. Я не позволяю элементу управления сортировкой jQueryUI изменять DOM вообще.

Как это возможно? У метода jQuery-ui sortable() есть вызов cancel, который устанавливает интерфейс пользователя так, как это было до того, как вы начали перетаскивать вещи. Хитрость заключается в том, чтобы прочитать состояние сортируемого элемента управления до того, как вы выполните этот вызов cancel. Таким образом, мы можем подобрать то, что было намерение пользователя, до того, как вызов cancel устанавливает DOM так, как он был. Как только у нас появятся эти намерения, мы можем передать их обратно в Реакт и манипулировать данными состояния, чтобы они были в новом порядке, который хотел пользователь. Наконец, вызовите setState() для этих данных, чтобы React отобразил новый порядок.

Вот как я это делаю:

  • Прикрепите метод jquery-ui.sortable() к списку позиций (генерируемых React конечно!)
  • Позвольте пользователю перетащить те позиции вокруг DOM.
  • Когда пользователь начинает перетаскивать, мы читаем индекс позиции, из которой пользователь перетаскивает.
  • Когда пользователь удаляет позицию, мы:
    • Прочитайте из jQuery-ui.sortable() новую позицию индекса для позиции, то есть где в списке пользователь сбросил ее.
    • Передайте вызов cancel в jQuery-ui.sortable(), чтобы список возвращался в исходное положение, а DOM не изменился.
    • Передайте старые и новые индексы перетаскиваемой позиции в качестве параметров функции JavaScript в модуле React.
    • Эта функция переупорядочивает данные состояния конечного состояния списка в новом порядке, который пользователь перетаскивал и вбрасывал.
    • Вызвать вызов setState().

Список в пользовательском интерфейсе теперь будет отражать новый порядок наших данных состояния; это стандартная функция React.

Итак, мы получаем возможность использовать функцию jQueryUI Sortable drag-and-drop, но без изменения DOM. Реагируйте довольным, потому что он контролирует DOM (где он должен быть).

Пример хранилища Github в https://github.com/brownieboy/react-dragdrop-test-simple. Это включает в себя ссылку на живую демонстрацию.

Ответ 4

Относительно длинного ответа Калояна Косева, я должен создать компонент для каждой функции jQueryUi, которую я хочу использовать? Нет, спасибо! Почему бы просто не обновить состояние при изменении DOM? Followig работает для меня:

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.

}

Элементу list нужен дополнительный атрибут, позволяющий jQuery выбирать элемент.

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

Для ids я использую UUID, поэтому у меня есть конфликты при сопоставлении их в orderSortableListsInState().