如何在React中使用jQuery UI?我看过Googling的一些例子,但所有这些例子似乎都已经过时了。
答案 0 :(得分:75)
如果真的需要这样做,这是我正在使用的方法。
计划:创建一个组件来管理jQuery插件。该组件将提供jQuery组件的以React为中心的视图。而且,它会:
props
作为插件配置选项并连接到插件的方法事件; 让我们通过jQuery UI Sortable插件探索一个如何做到这一点的实际示例。
如果您只想获取包装的jQuery UI可排序示例的最终版本:
...另外,以下缩短了较长的评论代码段:
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'));
对于那些想要了解为什么和如何的人。这是一步一步的指南:
我们的组件将接受项目(字符串)的数组(列表)为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>
);
}
};
我们想说我们要配置the opacity of the helper while sorting。我们将在插件配置中使用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 UI Sortable options。
你很可能需要连接一些插件方法,以便执行一些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) } />
在ReactJS在实际DOM中添加元素之后,我们需要将未来的控制传递给jQuery。否则,ReactJS永远不会重新渲染我们的组件,但我们不希望这样。我们希望jQuery负责所有更新。
React生命周期方法得到了拯救!
使用shouldComponentUpdate()让React知道组件的输出是否不受当前状态或道具更改的影响。默认行为是在每次状态更改时重新呈现,在绝大多数情况下,但我们不想要这种行为!
在收到新的道具或州时,在渲染之前调用 shouldComponentUpdate()
。如果shouldComponentUpdate()
返回false
,则不会调用componentWillUpdate()
,render()
和componentDidUpdate()
。
然后,我们使用componentWillReceiveProps()
,我们将this.props
与nextProps
进行比较,并仅在必要时调用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
};
许多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效果最佳。在这些情况下,React组件更像是第三方库的包装器。主要通过使用componentDidMount / componentWillUnmount来初始化/销毁第三方库。而道具作为一种方式,为父母提供了一种方法,可以自定义孩子包装的第三方库的行为,并与插件事件挂钩。
您可以使用此方法集成几乎所有jQuery插件!
答案 1 :(得分:12)
React并不适用于直接进行DOM突变的文库。如果其他东西改变了React试图渲染的DOM,它将抛出错误。如果你有来完成这项工作,你最好的妥协是让你的页面的不同部分由不同的东西管理,例如包含你的jquery组件的div,然后是其他一些div包含您的React组件。然而,在这些不同的(jquery和反应)组件之间进行通信将是困难的,而且说实话,选择其中一个可能更好。
答案 2 :(得分:7)
虽然技术上完美无缺,但Kayolan的答案有一个致命的缺陷,恕我直言:在将未来UI更新的责任从React传递给jQuery时,他反而忽略了React首先出现在那里的观点! React控制可排序列表的初始呈现,但在此之后,一旦用户执行第一次jQueryUI拖动/排序操作,React的状态数据就会过时。 React的重点是在视图级别表示您的状态数据。
所以,当我解决这个问题时,我采取了相反的方法:我试图确保React尽可能地控制。我不让jQueryUI Sortable控件完全改变DOM 。
怎么可能?好吧jQuery-ui的sortable()方法有一个cancel
调用,它将UI设置回你开始拖放之前的状态。诀窍是在发出cancel
调用之前读取可排序控件的状态。这样,我们可以在cancel
调用将DOM设置回原来的状态之前,获取用户的意图的内容。一旦我们有了这些意图,我们就可以将它们传递给React,并将状态数据操作为用户想要的新顺序。最后,在该数据上调用setState()以使React呈现新的顺序。
我是这样做的:
cancel
调用传递给jQuery-ui.sortable(),以使列表返回其原始位置,并且DOM保持不变。setState()
来电。UI中的列表现在将反映我们的州数据的新顺序;这是标准的React功能。
因此,我们可以使用jQueryUI Sortable的拖放功能,但是根本不需要更改DOM。 React很高兴,因为它控制着DOM(应该在哪里)。
https://github.com/brownieboy/react-dragdrop-test-simple处的Github存储库示例。这包括现场演示的链接。
答案 3 :(得分:1)
关于Kaloyan Kosev's long answer,我必须为我想要使用的每个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>
);
}
}
对于ID,我使用UUID,所以我在orderSortableListsInState()
中匹配时没有冲突。
答案 4 :(得分:0)
我无法使jquery-ui npm包正常工作。对我有用的是使用jquery-ui-bundle:
import $ from 'jquery';
import 'jquery-ui-bundle';
import 'jquery-ui-bundle/jquery-ui.min.css';