解决方法
计划:创建一个组件来管理jQuery插件。该组件将提供一个以React为中心的jQuery组件视图。此外,它将:
>使用React生命周期方法来初始化和拆除jQuery插件;
>使用React props作为插件配置选项,并连接插件的方法事件;
>当组件卸载时销毁插件。
让我们来探索一个实用的例子,如何用jQuery UI Sortable插件来做到这一点。
TLDR:最终版本
如果你只是想抓住最终版本的jQuery UI Sortable示例:
>这里是一个GIST I made与注释的注释;
>而这里是一个jsfiddle DEMO,全注释的评论也;
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:创建组件。
我们的组件将接受一个数组(列表)的项(字符串)作为数据支持。
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:通过道具传递配置选项
假设我们要配置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的任何一个。
步骤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) }); } // ... 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!',ui) } />
步骤4:将未来的更新控件传递给jQuery
在ReactJS将元素添加到实际的DOM之后,我们需要将未来的控件传递给jQuery。否则,ReactJS将永远不会重新渲染我们的组件,但是我们不希望这样做。我们希望jQuery负责所有更新。
反应生命周期的方法来拯救!
使用shouldComponentUpdate()让React知道组件的输出是否不受当前状态或道具的更改的影响。默认行为是在每个状态变化时重新呈现,绝大多数情况下,我们不希望这种行为!
在接收到新的道具或状态时,将在渲染之前调用shouldComponentUpdate()。如果shouldComponentUpdate()返回false,那么将不会调用componentWillUpdate(),render()和componentDidUpdate()。
然后,我们使用componentWillReceiveProps(),我们将this.props与nextProps进行比较,并在必要时调用jQuery UI排序更新。对于这个例子,我们将实现jQuery UI Sortable的enable / disable选项。
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 };
结论
用React包装jQuery插件并不总是最好的选择。但是,很高兴知道这是一个选项,以及如何实现解决方案。如果您将旧版jQuery应用程序迁移到React,或者您根本找不到适合您需求的React插件,那么这是一个可行的选择。
在图书馆修改DOM的情况下,我们尝试保持“反应”的方式。当DOM完全控制DOM时,React的效果最好。在这些情况下,React组件更多是第三方库的包装器。主要通过使用componentDidMount / componentWillUnmount来初始化/销毁第三方库。道具作为一种给父母一种定制孩子包装的第三方图书馆的行为并挂接插件事件的方法。