-
-
Save superKalo/a7b1fa9d68fbae6cdbf66be7d0588937 to your computer and use it in GitHub Desktop.
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({ | |
// Get the incoming `opacity` prop and use it in the plugin configuration | |
opacity: this.props.opacity, | |
// Get the incoming onChange func and we invoke it on the Sortable `change` event | |
change: (event, ui) => this.props.onChange(event, ui) | |
}); | |
} | |
// 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'); | |
} | |
} | |
// 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> | |
); | |
} | |
componentWillUnmount() { | |
// Clean up the mess when the component unmounts | |
this.$node.sortable('destroy'); | |
} | |
}; | |
// Optional: set the default props, in case none are passed | |
Sortable.defaultProps = { opacity: 1, enable: true }; | |
// Optional: set the prop types | |
Sortable.propTypes = { | |
opacity: React.PropTypes.number, | |
enable: React.PropTypes.bool, | |
onChange: React.PropTypes.func.isRequired | |
}; | |
export default Sortable; |
// 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) { | |
// Attach any custom behavior here | |
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')); |
@tarkancorak in the jsfidle, jquery and jqueryui are loaded in the global namespace like react is, so you do not need to import them because they are already available
Hi! how import jquery and jquery-ui in my component? I have a problem with this.
@superKalo Do you also have a solution for connected sortables? Drag and drop from a list to another.
This was extremely helpful for me, as I am migrating from a server-side rendered interface to React and didn't want to re-create my UI.
I did have an issue with:
this.$node.sortable('destroy');
I consistently got a Javascript error for this method.
My workaround was:
this.$node.remove()
I don't know if it's strictly necessary or even helpful.
@tarkancorak i love you so much man, I spent like 10 hours now trying to figure this import out
@tarkancorak love you too man, spent two hours trying to figure out the import. Finally worked!!!!
Hi
I used your solution for the Draggable component but following code returns an error:
Uncaught TypeError: _this4.props.onDrag is not a function
Can you help me?
`
class DragAndDrop extends Component {
constructor(props) {
super(props);
this.state = {
idImg: null,
idItem: null
};
this.handleOnDrag = this.handleOnDrag.bind(this);
this.handleOnDrop = this.handleOnDrop.bind(this);
}
render() {
return(
< img src="/images/1.jpg" id="img-1" className="img" onDrag={this.handleOnDrag} />
< div id="item-1" className="item" onDrop={this.handleOnDrop} > < /div>
)
}
handleOnDrag() {
this.setState({ idImg: $(this).attr("id") });
}
handleOnDrop() {
this.setState({ idItem: $(this).attr("id") });
}
componentDidMount() {
$(".img").draggable({
snap: ".item",
drag: (event, ui) => this.props.onDrag()
});
$(".item").droppable({
drop: (event, ui) => this.props.onDrop()
});
}
}
`
can you give an example on integrating to a funtional component also
Nice article. Thank you! However I'm wondering why this works on https://jsfiddle.net/superKalo/x7dxbrw4/ without any import/export statements.
In a node.js environment you'll have to add the import/export statements. In case anyone else needs that, I added some instructions below:
Install the jquery npm packages: npm i jquery jquery-ui --save
Than import the jquery stuff and export the component Sortable:
`import $ from 'jquery';
import 'jquery-ui/ui/widgets/sortable';
export default class Sortable extends React.Component {
...
...
}`