Open In App

Drag and Drop Sortable List Using ReactJS

Last Updated : 13 Oct, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Building a drag and drop sortable list in ReactJS allows users to rearrange items interactively by dragging and dropping. This feature enhances the user experience by providing dynamic way to manage lists or items. In this article, we’ll explore how to implement a drag and drop sortable list in ReactJS.

Output Preview

Drag and Drop Sortable List Using ReactJS

Prerequisites

Approach

  • This React-based application, "Drag and Drop Sortable List," allows users to reorder a list of items effortlessly. Users can drag and drop items within the list to change their order.
  • The constructor se­rves to initialize various aspects of the­ component's state. These­ include the list of items, the­ currently dragged item, and input fie­lds for adding new items.
  • The me­thod handleDragStart is responsible for se­tting up the dragging item and enabling its draggable­ property. On the other hand, handle­DragEnd clears the dragging item once­ it has been rele­ased.
  • To allow item drops, handleDragOve­r comes into play. If a drop is valid, handleDrop takes care­ of rearranging the items accordingly. Additionally, both handle­NameChange and handleImage­Change update values in input fie­lds.

Steps To Implement Drag and Drop Sortable List

Step 1: Create a react application by using this command

npx create-react-app  image-color-picker
cd image-color-picker

Step 2: Install required dependences

npm install react-icons --save

Project Structure

Folder Structure

Dependencies

"dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
    "react-icons": "^4.11.0"
  }

Example: Write the below code in App.js file and App.css in the src directory

CSS
/*App.css*/

@import url(
'https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: 'Poppins', sans-serif;
}

body {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: #f4f4f4;
}

.sortable-list {
    width: 425px;
    background: #ffffff;
    border-radius: 10px;
    padding: 20px;
    box-shadow: 0 13px 46px rgba(0, 0, 0, 0.1);
}

.input-field {
    padding: 10px;
    margin: 8px 0;
    border: 1px solid #ccc;
    border-radius: 14px;
    font-size: 16px;
    width: 100%;
    box-shadow: 0 3px 16px rgba(0, 0, 0, 0.1);
}

.add-button {
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 14px;
    padding: 10px 20px;
    cursor: pointer;
    font-size: 16px;
    width: 100%;
    transition: background-color 0.3s ease;
    box-shadow: 0 3px 16px rgba(0, 0, 0, 0.1);
}

.add-button:hover {
    background-color: #0056b3;
}

.sortable-list .item {
    margin-top: 1rem;
    border: 1px solid #ccc;
    justify-content: space-between;
    list-style: none;
    display: flex;
    cursor: move;
    background: #ffffff;
    align-items: center;
    border-radius: 10px;
    padding: 15px;
    margin-bottom: 10px;
    box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
}

.item .details {
    display: flex;
    align-items: center;
}

.item .details img {
    height: 60px;
    width: 60px;
    pointer-events: none;
    margin-right: 15px;
    object-fit: cover;
    border-radius: 50%;
}

.item .details span {
    font-size: 1.2rem;
    font-weight: 500;
    color: #333;
}

.item i {
    color: #474747;
    font-size: 1.13rem;
}

.item.dragging {
    opacity: 0.6;
}

.item.dragging .details,
.item.dragging i {
    opacity: 0.8;
    transform: scale(1.02);
    background: #f0f0f0;
}
JavaScript
//App.js

import React, { Component } from 'react';
import './App.css';
import { RiDragMove2Line } from 'react-icons/ri';

class App extends Component {
    constructor(props) {
        super(props);
        this.state = {
            items: [
                {
                    id: 1,
                    name: 'Kristina Zasiadko',
                    image:
                        'https://media.geeksforgeeks.org/wp-content/uploads/20230816223829/
                        geeksgforgeeks-logo-1.png',
                },
                {
                    id: 2,
                    name: 'John Doe',
                    image:
'https://media.geeksforgeeks.org/wp-content/uploads/20230721212159/gfg-logo.jpeg',
                },
                {
                    id: 3,
                    name: 'Jane Smith',
                    image:
'https://media.geeksforgeeks.org/wp-content/uploads/20230909123918/GeeksforGeeks-Wide-logo-black.png',
                },
                // Add more items here
            ],
            draggingItem: null,
            newItemName: '',
            newItemImage: '',
        };
    }

    handleDragStart = (e, item) => {
        this.setState({ draggingItem: item });
        e.dataTransfer.setData('text/plain', '');
    };

    handleDragEnd = () => {
        this.setState({ draggingItem: null });
    };

    handleDragOver = (e) => {
        e.preventDefault();
    };

    handleDrop = (e, targetItem) => {
        const { draggingItem, items } = this.state;
        if (!draggingItem) return;

        const currentIndex = items.indexOf(draggingItem);
        const targetIndex = items.indexOf(targetItem);

        if (currentIndex !== -1 && targetIndex !== -1) {
            items.splice(currentIndex, 1);
            items.splice(targetIndex, 0, draggingItem);
            this.setState({ items });
        }
    };

    handleNameChange = (e) => {
        this.setState({ newItemName: e.target.value });
    };

    handleImageChange = (e) => {
        this.setState({ newItemImage: e.target.value });
    };

    addNewItem = () => {
        
        // Generate a unique ID for the new item
        const newItemId = 
            Math.max(...this.state.items.map((item) 
                => item.id)) + 1;
        const newItem = {
            id: newItemId,
            name: this.state.newItemName,
            image: this.state.newItemImage,
        };

        // Add the new item to the state
        this.setState({
            items: [...this.state.items, newItem],
            newItemName: '', // Clear the input fields
            newItemImage: '',
        });
    };

    render() {
        return (
            <div className="sortable-list">
                <div className="new-item">
                    <input
                        type="text"
                        placeholder="Name"
                        value={this.state.newItemName}
                        onChange={this.handleNameChange}
                        className="input-field"
                    />
                    <input
                        type="text"
                        placeholder="Image URL"
                        value={this.state.newItemImage}
                        onChange={this.handleImageChange}
                        className="input-field"
                    />
                    <button onClick={this.addNewItem} 
                            className="add-button">
                        Add New Item
                    </button>
                </div>
                {this.state.items.map((item, index) => (
                    <div
                        key={item.id}
                        className=
                            {`item ${item === this.state.draggingItem ? 
                                'dragging' : ''
                            }`}
                        draggable="true"
                        onDragStart={(e) => 
                            this.handleDragStart(e, item)}
                        onDragEnd={this.handleDragEnd}
                        onDragOver={this.handleDragOver}
                        onDrop={(e) => this.handleDrop(e, item)}
                    >
                        <div className="details">
                            <img src={item.image} alt={item.name} />
                            <span>{item.name}</span>
                        </div>
                        
                        {/* Use the React icon component */}
                        <RiDragMove2Line /> 
                    </div>
                ))}
            </div>
        );
    }
}

export default App;


To start the application run the following command.

npm start

Output


Next Article

Similar Reads