【问题标题】:How to use jQuery UI with React JS如何在 React JS 中使用 jQuery UI
【发布时间】:2016-12-14 15:53:48
【问题描述】:

如何在 React 中使用 jQuery UI?我在谷歌上看过几个例子,但似乎都已经过时了。

【问题讨论】:

  • 是的,我必须这样做。对于一个项目,我必须使用 jquery ui 中的很多组件。
  • 因为我还必须管理很多状态? @azium

标签: jquery-ui reactjs


【解决方案1】:

我无法让 jquery-ui npm 包工作。对我有用的是使用 jquery-ui-bundle:

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

【讨论】:

  • 老兄,经过这么多尝试,这对我有用。谢谢!
【解决方案2】:

关于Kaloyan Kosev's long answer,我必须为我想使用的每个jQueryUi 功能创建一个组件吗?不用了,谢谢!当您更改 DOM 时,为什么不简单地更新您的 state? 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.

}

列表元素只需要一个额外的属性让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>
        );
    }
}

对于 id,我使用 UUID,所以在 orderSortableListsInState() 中匹配它们时我没有冲突。

【讨论】:

    【解决方案3】:

    虽然技术上完美无缺,但 Kayolan 的回答有一个致命的缺陷,恕我直言:在将未来 UI 更新的责任从 React 转移到 jQuery 时,他首先否定了 React 存在的意义! React 控制可排序列表的初始渲染,但之后一旦用户执行第一个 jQueryUI 拖动/排序操作,React 的状态数据就会过时。 React 的全部意义在于在视图级别表示您的状态数据。

    所以,当我处理这个问题时,我采取了相反的方法:我试图确保 React 尽可能地处于控制之中。我不让 jQueryUI 可排序控件改变 DOM

    这怎么可能? jQuery-ui 的 sortable() 方法有一个 cancel 调用,可以将 UI 设置为开始拖放之前的状态。诀窍是在发出cancel 调用之前读取可排序控件的状态。这样,我们可以在cancel 调用将DOM 恢复到原来的状态之前了解用户的意图。一旦我们有了这些意图,我们就可以将它们传递回 React,并按照用户想要的新顺序操作状态数据。最后,在该数据上调用 setState() 以让 React 呈现新订单。

    我是这样做的:

    1. 将 jquery-ui.sortable() 方法附加到行项目列表(当然由 React 生成!)
    2. 让用户在 DOM 周围拖放这些行项目。
    3. 当用户开始拖动时,我们会读取用户拖动的行项目的索引。
    4. 当用户放弃订单项时,我们:
      1. 从 jQuery-ui.sortable() 中读取行项目的新索引位置,即用户在列表中丢弃它的位置。
      2. cancel 调用传递给 jQuery-ui.sortable() 以便列表返回到其原始位置,并且 DOM 保持不变。
      3. 将拖动的行项目的新旧索引作为参数传递给 React 模块中的 JavaScript 函数。
      4. 让该函数将列表的后端状态数据重新排序为用户将其拖放到的新顺序。
      5. 拨打 React setState() 电话。

    UI 中的列表现在将反映我们状态数据的新顺序;这是标准的 React 功能。

    因此,我们可以使用 jQueryUI Sortable 的拖放功能,但根本不需要更改 DOM。 React 很高兴,因为它控制着 DOM(它应该在哪里)。

    https://github.com/brownieboy/react-dragdrop-test-simple 上的 Github 存储库示例。这包括一个现场演示的链接。

    【讨论】:

    • 我喜欢这两个答案。你的答案是我如何在几年前实现 Knockout.js 和 jQuery UI 之间的可排序集成,这使得 KO 和 Virtual DOM 实现(如 React)都很高兴。另一方面,很高兴知道当我们无法取消第三方库对 DOM 的更改时,我们有一个回退机制。
    • 一个非常好的解决方案,它迫使人们思考反应方式而不是思考旧方式。我应该承认,我得出了与 Kaloyan 相同的结论,并且从那以后一直在使用类似的技术。让反应控制渲染意味着每个不同组件的手动工作,但会产生最好的结果。简单地包装组件有可能引入细微的错误(我证明了这一点)。这个答案应该会得到更多的选票。
    • 具有与此答案相同的概念。一些简单的组件(例如日历)可以使用内置的道具/事件功能将它们的状态与 Reace/Vue/Angular 同步。示例:vuejsdevelopers.com/2017/05/20/vue-js-safely-jquery-plugin
    【解决方案4】:

    如果您真的需要这样做,这是我正在使用的一种方法。

    计划:创建一个组件来管理jQuery插件。这个组件将提供一个以 React 为中心的 jQuery 组件视图。此外,它将:

    • 使用 React 生命周期方法初始化和拆除 jQuery 插件;
    • 使用 React props 作为插件配置选项并连接到插件的方法事件;
    • 卸载组件时销毁插件。

    让我们探索一个实际示例,如何使用jQuery UI Sortable 插件来做到这一点。


    TLDR:最终版本

    如果您只想获取包装好的 jQuery UI 可排序示例的最终版本:

    ...另外,下面是 从较长的 cmets 中缩短的代码 sn-p:

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

    ... 下面是如何使用&lt;Sortable /&gt; 组件:

    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 步:通过 props 传递配置选项

    假设我们要配置the opacity of the helper while sorting。我们将在插件配置中使用opacity 选项,它的值从0.011

    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 UI Sortable options

    第 3 步:插件事件的挂钩函数。

    你很可能需要连接一些插件方法,以便执行一些 React 逻辑,例如,操纵状态。

    这是如何做到这一点的:

    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 负责所有更新。

    React 生命周期方法来救援!

    使用 shouldComponentUpdate() 让 React 知道组件的输出是否不受当前状态或道具变化的影响。默认行为是在每次状态更改时重新渲染,绝大多数情况下,但我们不希望这种行为!

    shouldComponentUpdate() 在接收新道具或状态时在渲染之前调用。如果shouldComponentUpdate() 返回false,则不会调用componentWillUpdate()render()componentDidUpdate()

    然后,我们使用componentWillReceiveProps(),将this.propsnextProps 进行比较,并仅在必要时调用jQuery UI 可排序更新。对于这个例子,我们将实现 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 事件并销毁。 React 生命周期方法再次派上用场,并提供了一种在组件卸载时挂钩的机制。

    class Sortable extends React.Component {
        // ... omitted for brevity
    
        componentWillUnmount() {
            // Clean up the mess when the component unmounts
            this.$node.sortable('destroy');
        }
    
        // ... omitted for brevity
    };
    

    结论

    用 React 封装 jQuery 插件并不总是最好的选择。但是,很高兴知道这是一个选项以及如何实施解决方案。如果您正在将遗留的 jQuery 应用程序迁移到 React,或者您找不到适合您的需求的 React 插件,这是一个可行的选择。

    在库修改 DOM 的情况下,我们会尽量让 React 不受影响。 React 在完全控制 DOM 时效果最好。在这些情况下,React 组件更像是 3rd 方库的包装器。主要是通过使用 componentDidMount/componentWillUnmount 来初始化/销毁第三方库。并且 props 是一种为父级提供自定义子级包装的第三方库的行为并连接插件事件的方法。

    您可以使用这种方法集成几乎所有的 jQuery 插件

    【讨论】:

    • 您知道如何使用操作 DOM 的组件吗?例如 Jquery UI sortable 和其他操作 DOM 的库。
    • @Luke101 我编辑了我的答案,现在我使用 jQuery UI Sortable 作为示例。在库修改 DOM 的情况下,就像 Sortable 所做的那样,我们试图让 React 远离它。 React 在完全控制 DOM 时效果最好。在这些情况下,React 组件更像是 3rd 方库的包装器(就像我展示的那样)。我还用演示做了一个 jsfiddle:jsfiddle.net/superKalo/x7dxbrw4
    • 您的组件中缺少某些内容。您根本没有向我们展示您在哪里导入 jQuery 插件。
    【解决方案5】:

    React 不能很好地与直接进行 DOM 突变的库配合使用。如果其他东西改变了 React 试图渲染的 DOM,它会抛出错误。如果您不得不完成这项工作,那么最好的折衷办法是让页面的不同部分由不同的事物管理,例如包含 jquery 组件的 div,然后是其他一些div 包含你的 React 组件。在这些不同的(jquery 和 react)组件之间进行通信会很困难,但是老实说,最好选择其中一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 2023-04-02
      • 1970-01-01
      • 2020-09-19
      • 1970-01-01
      • 2022-04-07
      • 2018-08-07
      相关资源
      最近更新 更多