A React component built on top of Sortable (https://github.com/RubaXa/Sortable).
The sample code can be found in the examples directory.
There is a major breaking change since v1.0. Checkout Migration Guide while upgrading from earlier versions.
The easiest way to use react-sortablejs is to install it from npm and include it in your React build process using webpack or browserify.
npm install --save react react-dom sortablejs@1.6.1 # Install peerDependencies
npm install --save react-sortablejs
Checkout the examples directory for a complete setup.
You can create a standalone ES5 module as shown below:
$ git clone https://github.com/cheton/react-sortable.git
$ cd react-sortable
$ npm install
$ npm run build && npm run dist
Then, include these scripts into your html file:
<body>
<div id="container"></div>
<script src="http://fb.me/react-0.14.7.js"></script>
<script src="http://fb.me/react-dom-0.14.7.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/Sortable/1.4.2/Sortable.min.js"></script>
<script src="dist/react-sortable.min.js"></script>
</body>
Use <ReactSortable /> instead of <Sortable /> in your JSX code since the Sortable library will export a window.Sortable object if you're running JSX code directly in the browser. For example:
<ReactSortable
tag="ul"
onChange={(order) =>
this.props.onChange(order);
}}
>
{items}
</ReactSortable>
File: sortable-list.jsx
import uniqueId from 'lodash/uniqueId';
import React from 'react';
import Sortable from 'react-sortablejs';
// Functional Component
const SortableList = ({ items, onChange }) => {
let sortable = null; // sortable instance
const reverseOrder = (evt) => {
const order = sortable.toArray();
onChange(order.reverse());
};
const listItems = items.map(val => (<li key={uniqueId()} data-id={val}>List Item: {val}</li>));
return (
<div>
<button type="button" onClick={reverseOrder}>Reverse Order</button>
<Sortable
// Sortable options (https://github.com/RubaXa/Sortable#options)
options={{
}}
// [Optional] Use ref to get the sortable instance
// https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute
ref={(c) => {
if (c) {
sortable = c.sortable;
}
}}
// [Optional] A tag to specify the wrapping element. Defaults to "div".
tag="ul"
// [Optional] The onChange method allows you to implement a controlled component and keep
// DOM nodes untouched. You have to change state to re-render the component.
// @param {Array} order An ordered array of items defined by the `data-id` attribute.
// @param {Object} sortable The sortable instance.
// @param {Event} evt The event object.
onChange={(order, sortable, evt) => {
onChange(order);
}}
>
{listItems}
</Sortable>
</div>
);
};
SortableList.propTypes = {
items: React.PropTypes.array,
onChange: React.PropTypes.func
};
export default SortableList;
File: index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import SortableList from './sortable-list';
class App extends React.Component {
state = {
items: [1, 2, 3, 4, 5, 6]
};
render() {
return (
<SortableList
items={this.state.items}
onChange={(items) => {
this.setState({ items });
}}
>
</SortableList>
)
}
};
ReactDOM.render(
<App />,
document.getElementById('container')
);
An uncontrolled component allows Sortable to touch DOM nodes. It's useful when you don't need to maintain any state changes.
import uniqueId from 'lodash/uniqueId';
import React from 'react';
import ReactDOM from 'react-dom';
import Sortable from 'react-sortablejs';
class App extends React.Component {
state = {
items: ['Apple', 'Banana', 'Cherry', 'Guava', 'Peach', 'Strawberry']
};
render() {
const items = this.state.items.map(val => (<li key={uniqueId()} data-id={val}>{val}</li>));
return (
<div>
<Sortable
tag="ul" // Defaults to "div"
>
{items}
</Sortable>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
A controlled component will keep DOM nodes untouched. You have to change state to re-render the component.
import uniqueId from 'lodash/uniqueId';
import React from 'react';
import ReactDOM from 'react-dom';
import Sortable from '../src';
class App extends React.Component {
state = {
items: ['Apple', 'Banana', 'Cherry', 'Guava', 'Peach', 'Strawberry']
};
render() {
const items = this.state.items.map(val => (<li key={uniqueId()} data-id={val}>{val}</li>));
return (
<div>
<Sortable
tag="ul" // Defaults to "div"
onChange={(order, sortable, evt) => {
this.setState({ items: order });
}}
>
{items}
</Sortable>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
An example of using the group
option to drag elements from one list into another.
File: shared-group.jsx
import uniqueId from 'lodash/uniqueId';
import React from 'react';
import Sortable from '../src';
// Functional Component
const SharedGroup = ({ items }) => {
items = items.map(val => (<li key={uniqueId()} data-id={val}>{val}</li>));
return (
<Sortable
// See all Sortable options at https://github.com/RubaXa/Sortable#options
options={{
group: 'shared'
}}
tag="ul"
>
{items}
</Sortable>
);
};
export default SharedGroup;
File: index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import SharedGroup from './shared-group';
const App = (props) => {
return (
<div>
<SharedGroup
items={['Apple', 'Banaba', 'Cherry', 'Grape']}
/>
<br/>
<SharedGroup
items={['Lemon', 'Orange', 'Pear', 'Peach']}
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('container'));