Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use jQuery UI with React JS

How can I use jQuery UI with React? I have seen a couple examples by Googling, but all of them seem to be outdated.

like image 663
Tahnik Mustasin Avatar asked Aug 08 '16 18:08

Tahnik Mustasin


People also ask

How use jQuery UI With React?

Use React lifecycle methods to initialize and tear down the jQuery plugin; Use React props as plugin configuration options and hook up to plugin's methods events; Destroy the plugin when component unmounts.

Can we use jQuery in ReactJS?

Yes, we can use jQuery in ReactJs.

Why we cant use jQuery in React?

The main limitation of using jQuery in React is that jQuery manually updates the DOM. On the other hand, React has its system for making changes to the DOM. It internally determines when a React app or component should re-render. It also figures out which parts of the UI need to be updated.


2 Answers

React doesn't play well with libraries that do direct DOM mutations. If something else mutates the DOM where React is attempting to render, it will throw errors. If you had to make this work, your best compromise is to have different parts of your page which are managed by different things, for example a div which houses your jquery component(s), and then some other div which contains your React component(s). Communicating between these disparate (jquery and react) components will be difficult however and honestly it's probably better to just choose one or the other.

like image 34
John Avatar answered Sep 20 '22 14:09

John


If you really need to do that, here is an approach I am using.

The plan: Create a component to manage the jQuery plugin. This component will provide a React-centric view of the jQuery component. Moreover, it will:

  • Use React lifecycle methods to initialize and tear down the jQuery plugin;
  • Use React props as plugin configuration options and hook up to plugin's methods events;
  • Destroy the plugin when component unmounts.

Let's explore a practical example how to do that with the jQuery UI Sortable plugin.


TLDR: The Final Version

If you just want to grab the final version of the wrapped jQuery UI Sortable example:

  • here is a GIST I made with full annotated comments;
  • and here's a jsfiddle DEMO, full annotated comments too;

... plus, below is the shortened from the longer comments code snippet:

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

Optionally, you can set default props (in the case of none are passed) and the prop types:

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

... and here's how to use the <Sortable /> component:

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

The Full Explanation

For those of you, who want to understand why and how. Here's a step by step guide:

Step 1: Create a component.

Our component will accept an array (list) of items (strings) as 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>         );     } }; 

Step 2: Pass configuration options via props

Let's say we want to configure the opacity of the helper while sorting. We'll use the opacity option in the plugin configuration, that takes values from 0.01 to 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 }; 

And here's how we can use the component in our code now:

<Sortable opacity={0.8} /> 

The same way, we can map any of the jQUery UI Sortable options.

Step 3: Hook-up functions on plugin events.

You will most probably need to hook-up on some of the plugin methods, in order to perform some React logic, for example, manipulate the state let's day.

Here's how to do that:

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

And here's how to use it:

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

Step 4: Pass the future updates control to jQuery

Right after ReactJS adds the element in the actual DOM, we need to pass the future control to jQuery. Otherwise, ReactJS will never re-render our component, but we don't want that. We want jQuery to be responsible for all updates.

React lifecycle methods comes to the rescue!

Use shouldComponentUpdate() to let React know if a component's output is not affected by the current change in state or props. The default behavior is to re-render on every state change, and in the vast majority, but we don't want this behavior!

shouldComponentUpdate() is invoked before rendering when new props or state are being received. If shouldComponentUpdate() returns false, then componentWillUpdate(), render(), and componentDidUpdate() will not be invoked.

Then, we use componentWillReceiveProps(), we compare this.props with nextProps and call jQuery UI sortable updates only when necessary. For this example, we will implement the enable/disable option of the 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 }; 

Step 5: Clean up the mess.

Many jQuery plugins provide a mechanism for cleaning up after themselves when they are no longer needed. jQuery UI Sortable provides an event that we can trigger to tell the plugin to unbind its DOM events and destroy. React lifecycle methods comes to the rescue again and provides a mechanism to hook into when the component is being unmounted.

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

Wrapping jQuery plugins with React is not always the best choice. However, it is nice to know that it is an option and how you can implement a solution. It is a viable option if you are migrating a legacy jQuery application to React or maybe you just can't find a React plugin that suits your needs in your case.

In the case that a library modifies the DOM, we try to keep React out of its way. React works best when it has full control of the DOM. In these cases, React components are more of wrappers for the 3rd party libraries. Mostly by using the componentDidMount/componentWillUnmount to initialize/destroy the third party library. And props as a way of giving the parent a way of customizing the behavior of the third party library that the child wraps and to hook-up on plugin events.

You can use this approach to integrate almost any jQuery plugin!

like image 150
Kaloyan Kosev Avatar answered Sep 24 '22 14:09

Kaloyan Kosev