
Bagi kamu yang belum familiar dengan istilah Drag & Drop ini definisinya :
Drag & Drop adalah aksi untuk memindahkan suatu objek dengan cara mengklik kemudian menariknya, setelah itu diletakkan pada lokasi yang diinginkan. penarikan objek (Drag) dilakukan dengan menahan tombol sebelah kiri mouse, kemudian melepaskannya ketika akan meletakkan objek (Drop).
Tujuan dari drag & drop itu untuk memudahkan user dalam menggunakan aplikasi selain itu juga bisa mempercepat dalam melakukan sebuah aksi, sehingga menambah User Experience pada aplikasi yang kita buat.
Penerapan drag & drop yang sering kita temui adalah pada file manager, untuk mengimport gambar kedalam aplikasi editing seperti Adobe Photoshop kita hanya perlu menarik gambarnya dari file manager ke aplikasi editing tersebut, jadi kita tidak perlu mengimportnya secara manual karena akan memakan cukup banyak waktu apalagi jika gambar yang akan kita import sangat banyak.
Pada tutorial kali ini kita akan belajar bagaimana cara membuat Drag & Drop pada React JS, sebagai studi kasus, kita akan membuat aplikasi todo list sederhana yang memiliki 3 bagian, yaitu :
Todo 🔵
Doing ⏸
Completed ✅
masing-masing bagian tersebut nantinya bisa didrag & drop dan masing-masing bagian tersebut berisikan task-task yang bisa didrag & drop juga.
Aplikasi yang akan kita buat nanti hasil akhirnya seperti ini :
Hasil AkhirLink Demo : https://todo-dnd.now.sh/
Ohya untuk membuat Drag & Drop nya kita akan menggunakan library react-beautiful-dnd, kita menggunakan library tersebut karena ada beberapa alasan, yaitu :
Pergerakan item saat berpindah posisi sangat natural dan smooth 💐
Accessible: bisa berinteraksi melalui keyboard and mendukung screen reader ♿️
Performanya sangat baik 🚀
API nya lebih clean dan powerful sehingga mudah diimplementasikan
Bisa bekerja baik dengan interaksi browser standar
Tidak diperlukan DOM node pembungkus tambahan
Oke saya kira sudah cukup intronya, yuk langsung saja mulai membuatnya 😀
Pertama, kita buat dulu projectnya menggunakan CRA atau kalau mau setup manual pake webpack, babel, dkk juga boleh 🤣
1npx create-react-app todo-dnd
kemudian kita install library-library yang diperlukan :
1npm i react-beautiful-dnd styled-components @atlaskit/css-reset
Hapus semua file pada folder src/ kecuali App.js index.js dan serviceWorker.js setelah itu buka file index.js dan import library @atlaskit/css-reset untuk mereset tampilan default masing2 browser sehingga styling pada aplikasi kita akan lebih konsisten.
jangan lupa juga hapus import index.css sehingga isi dari file index.js kita akan menjadi seperti ini :
index.js1import React from 'react';2import ReactDOM from 'react-dom';3import App from './App';4import * as serviceWorker from './serviceWorker';5import '@atlaskit/css-reset';67ReactDOM.render(8 <React.StrictMode>9 <App />10 </React.StrictMode>,11 document.getElementById('root')12);1314// If you want your app to work offline and load faster, you can change15// unregister() to register() below. Note this comes with some pitfalls.16// Learn more about service workers: https://bit.ly/CRA-PWA17serviceWorker.unregister();
buat file baru bernama initialData.js di dalam folder src/ yang isinya seperti ini :
initialData.js1export const initialData = {2 tasks: {3 'task-1': { id: 'task-1', content: 'Learn React JS' },4 'task-2': { id: 'task-2', content: 'Learn Vue JS' },5 'task-3': { id: 'task-3', content: 'Learn Angular JS' },6 'task-4': { id: 'task-4', content: 'Learn Svelte JS' },7 },8 cards: {9 'card-1': {10 id: 'card-1',11 title: 'todo',12 taskIds: ['task-1', 'task-2', 'task-3', 'task-4'],13 color: '#FFBA08',14 },15 'card-2': {16 id: 'card-2',17 title: 'doing',18 taskIds: [],19 color: '#17C9FF',20 },21 'card-3': {22 id: 'card-3',23 title: 'completed',24 taskIds: [],25 color: '#14E668',26 },27 },28 cardOrder: ['card-1', 'card-2', 'card-3']29}
pada initial data yang telah kita buat, terdapat 3 buah object, yaitu :
**tasks** : berisi task-task
**cards** : berisi card, card ini fungsinya untuk menampung task, misalnya card todo, dll
**cardOrder** : ini adalah urutan dari card-card kita, pada kode diatas yang paling kiri adalah todo, yg tengah doing dan yang terakhir completed
replace isi dari App.js dengan kode berikut :
App.js1import React, {useState} from 'react';2import Card from './Card';3import styled from 'styled-components';4import {initialData} from './initialData.js';56const Title = styled.h1`7 color: #7B7B7B;8 font-family: sans-serif;9 font-size: 30px;10 text-align: center;11 padding-top: 25px;12`1314const CardContainer = styled.div`15 width: 100%;16 display: flex;17 justify-content: center;18 align-items: flex-start;19 margin-top: 25px;20`212223const App = () => {24 const [state, setState] = useState(initialData);2526 return (27 <React.Fragment>28 <Title>Drag & Drop React JS</Title>29 <CardContainer>30 {31 state.cardOrder.map((cardId, index) => {32 const card = state.cards[cardId];33 const tasks = card.taskIds.map(taskId => state.tasks[taskId]);34 return <Card key={cardId} card={card} tasks={tasks} index={index} />35 })36 }37 </CardContainer>38 </React.Fragment>39 )4041}4243export default App;
pada App.js kita mengimport initial data yang sudah kita buat tadi dan component Card yang akan kita buat setelah ini, di file tersebut kita juga menampilkan judul dan melakukan looping dari masing-masing cards yang akan ditampilkan di component Card.
buatlah file Card.jsx pada folder src/ yang berisi :
Card.jsx1import React from 'react';2import Task from './Task';3import styled from 'styled-components';45const CardContainer = styled.div`6 width: 300px;7 margin: 0px 25px;8 background: ${props => props.color};9 border-radius: 40px;10 padding: 15px;11 box-shadow: 25px 25px 50px rgba(0, 0, 0, 0.15);12`1314const CardTitle = styled.h3`15 color: #FFFFFF;16 text-align: center;17 margin-bottom: 25px;18 font-family: sans-serif;19 font-size: 25px;20 font-weight: bold;21`2223const TaskContainer = styled.div`24 min-height: 400px;25 width: 100%;26`272829function Card({card, tasks}) {30 return (31 <CardContainer color={card.color}>32 <CardTitle>33 #{card.title}34 </CardTitle>35 <TaskContainer>36 {tasks.map((task, index) => <Task key={task.id} task={task} index={index} /> )}37 </TaskContainer>38 </CardContainer>39 );40}4142export default Card;
kita menerima 2 props yaitu card berisi data detail card dan tasks berisi detail task-task pada data card, di component ini kita akan menampilkan judul card dan melakukan looping untuk menampilkan task-task yang terdapat pada card.
Task.jsx1import React from 'react';2import styled from 'styled-components';34const TaskList = styled.div`5 width: 100%;6 background: #FFFFFF;7 border-radius: 10px;8 margin-bottom: 15px;9 box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);10 cursor: pointer;11`1213const TaskListText = styled.h6`14 color: #7B7B7B;15 text-align: center;16 font-family: sans-serif;17 font-size: 20px;18 margin: 0px;19 padding: 15px;20 text-transform: none;21`2223function Task({task}) {24 return (25 <TaskList>26 <TaskListText>{task.content}</TaskListText>27 </TaskList>28 );29}3031export default Task;
pada component Task fungsinya sederhana yaitu hanya untuk menampilkan content dari task.
Sejauh ini kita sudah berhasil membuat layout dari aplikasi kita, coba masuk ke directory project nya kemudian jalankan aplikasinya maka tampilannya kurang lebih akan seperti ini :

App.js1import React, {useState} from 'react';2import Card from './Card';3import styled from 'styled-components';4import {initialData} from './initialData.js';5import {DragDropContext, Droppable} from 'react-beautiful-dnd';67const Title = styled.h1`8 color: #7B7B7B;9 font-family: sans-serif;10 font-size: 30px;11 text-align: center;12 padding-top: 25px;13`1415const CardContainer = styled.div`16 width: 100%;17 display: flex;18 justify-content: center;19 align-items: flex-start;20 margin-top: 25px;21`2223const App = () => {24 const [state, setState] = useState(initialData);2526 const onDragEnd = (result) => {27 const {draggableId, source, destination, type} = result;28 if ((!destination) || (source.droppableId === destination.droppableId && source.index === destination.index)) {29 return;30 }3132 if (type === "card") {33 const newCardOrder = Array.from(state.cardOrder);34 newCardOrder.splice(source.index, 1);35 newCardOrder.splice(destination.index, 0, draggableId);3637 const newState = {38 ...state,39 cardOrder: newCardOrder40 }41 setState(newState);42 return;43 }4445 if (type === "task") {46 const start = state.cards[source.droppableId];47 const finish = state.cards[destination.droppableId];4849 if (start === finish) {50 const card = state.cards[source.droppableId];51 const newTaskIds = Array.from(card.taskIds);52 newTaskIds.splice(source.index, 1);53 newTaskIds.splice(destination.index, 0, draggableId);54 const newCard = {55 ...card,56 taskIds: newTaskIds57 };58 const newState = {59 ...state,60 cards: {61 ...state.cards,62 [newCard.id]: newCard63 }64 }65 setState(newState);66 return67 }68 // move to another card69 const startTaskIds = Array.from(start.taskIds);70 startTaskIds.splice(source.index, 1);71 const newStart = {72 ...start,73 taskIds: startTaskIds74 }7576 const finishTaskIds = Array.from(finish.taskIds);77 finishTaskIds.splice(destination.index, 0, draggableId);78 const newFinish = {79 ...finish,80 taskIds: finishTaskIds81 }8283 const newState = {84 ...state,85 cards: {86 ...state.cards,87 [newStart.id]: newStart,88 [newFinish.id]: newFinish89 }90 }91 setState(newState);92 return;93 }94 }9596 return (97 <React.Fragment>98 <Title>Drag & Drop React JS</Title>99 <DragDropContext onDragEnd={onDragEnd}>100 <Droppable droppableId="all-cards" direction="horizontal" type="card">101 {(provided) => (102 <CardContainer ref={provided.innerRef} {...provided.droppableProps}>103 {104 state.cardOrder.map((cardId, index) => {105 const card = state.cards[cardId];106 const tasks = card.taskIds.map(taskId => state.tasks[taskId]);107 return <Card key={cardId} card={card} tasks={tasks} index={index} />108 })109 }110 {provided.placeholder}111 </CardContainer>112 )}113 </Droppable>114 </DragDropContext>115 </React.Fragment>116 )117118}119120export default App;
Penjelasan kode :
1...2<DragDropContext onDragEnd={onDragEnd}>3<Droppable droppableId="all-cards" direction="horizontal" type="card">4...
DragDropContext adalah pembungkus component2 yang ingin kita buat menjadi Drag & Drop
pada DragDropContext kita passing function onDragEnd untuk melakukan suatu aksi ketika user selesai melakukan Drag (penarikan objek).
Pada function onDragEnd, inti dari fungsinya adalah untuk meng-update state berdasarkan hasil dragging.
Droppable menandakan bahwa area tsb bisa didrop
props direction menandakan arah dari drag & drop nya, defaultnya adalah vertical dan untuk perpindahan antar Card itu dari kiri ke kanan atau kanan ke kiri (horizontal) maka harus kita definisikan sebagai horizontal
1...2{(provided) => (3...4{provided.placeholder}5...
variable provided : berisi data-data dari component Droppable, data ini seperti innerRef, droppableProps, placeholder, dll
provided.placeholder : untuk menyediakan space kosong saat item didrag
Card.jsx1import React from 'react';2import Task from './Task';3import styled from 'styled-components';4import {Droppable, Draggable} from 'react-beautiful-dnd';56const CardContainer = styled.div`7 width: 300px;8 margin: 0px 25px;9 background: ${props => props.color};10 border: ${props => (props.isDraggingOver ? '4px dashed #FFF' : '4px dashed rgba(0,0,0,0)')};11 border-radius: 40px;12 padding: 15px;13 box-shadow: 25px 25px 50px rgba(0, 0, 0, 0.15);14`1516const CardTitle = styled.h3`17 color: #FFFFFF;18 text-align: center;19 margin-bottom: 25px;20 font-family: sans-serif;21 font-size: 25px;22 font-weight: bold;23`2425const TaskContainer = styled.div`26 min-height: 400px;27 width: 100%;28`293031function Card({card, tasks, index}) {32 return (33 <Draggable draggableId={card.id} index={index}>34 {(provided) => (35 <Droppable droppableId={card.id} type="task">36 {(provided2, snapshot) => (37 <CardContainer ref={provided.innerRef} color={card.color} {...provided.dragHandleProps} isDraggingOver={snapshot.isDraggingOver} {...provided.draggableProps}>38 <CardTitle>39 #{card.title}40 </CardTitle>41 <TaskContainer ref={provided2.innerRef} {...provided2.droppableProps}>42 {tasks.map((task, index) => <Task key={task.id} task={task} index={index} /> )}43 {provided2.placeholder}44 </TaskContainer>45 </CardContainer>46 )}47 </Droppable>48 )}49 </Draggable>50 );51}5253export default Card;
Penjelasan kode :
1border: ${props => (props.isDraggingOver ? '4px dashed #FFF' : '4px dashed rgba(0,0,0,0)')};2...3isDraggingOver={snapshot.isDraggingOver}
kode diatas berfungsi untuk memunculkan border berwarna putih pada Card ketika salah satu Task didrag tepat diatas Card tsb.
Dashed Border1<Draggable draggableId={card.id} index={index}>
Draggable menandakan bahwa suatu component bisa diDrag pada contoh diatas adalah component Card
1{...provided.dragHandleProps}
kode diatas berfungsi agar componentnya bisa melakukan Dragging. kalau kamu pengen ngeDrag nya pakai button tambahan maka pindahkan kode diatas ke button tsb.
Task.jsx1import React from 'react';2import styled from 'styled-components';3import {Draggable} from 'react-beautiful-dnd';45const TaskList = styled.div`6 width: 100%;7 background: #FFFFFF;8 border-radius: 10px;9 margin-bottom: 15px;10 box-shadow: ${props => props.isDragging ? '0px 10px 20px rgba(0, 0, 0, 0.25)' : '0px 4px 4px rgba(0, 0, 0, 0.25)'};11 cursor: pointer;12`1314const TaskListText = styled.h6`15 color: #7B7B7B;16 text-align: center;17 font-family: sans-serif;18 font-size: 20px;19 margin: 0px;20 padding: 15px;21 text-transform: none;22`2324function Task({task, index}) {25 return (26 <Draggable draggableId={task.id} index={index}>27 {(provided, snapshot) => (28 <TaskList ref={provided.innerRef} isDragging={snapshot.isDragging} {...provided.draggableProps} {...provided.dragHandleProps}>29 <TaskListText>{task.content}</TaskListText>30 </TaskList>31 )}32 </Draggable>33 );34}3536export default Task;
Penjelasan kode :
1box-shadow: ${props => props.isDragging ? '0px 10px 20px rgba(0, 0, 0, 0.25)' : '0px 4px 4px rgba(0, 0, 0, 0.25)'};2...3isDragging={snapshot.isDragging}
kode diatas berfungsi untuk memperbesar shadow pada component Task ketika component Task tersebut sedang didrag.
Selamat, kita telah berhasil membuat drag & drop pada React JS menggunakan react-beautiful-dnd 🥳🥳
Ohya untuk Source code nya bisa kamu download di sini : https://github.com/alfinsyahruddin/todo-dnd
Kalau kamu suka artikel ini jangan lupa tinggalkan claps nya 👏👏 dan jika kamu rasa artikel ini bermanfaat silakan Share ke teman-teman kamu! Terimakasih… 😅🙏
Oke mungkin itu saja yang bisa saya bagikan kali ini, kalau kamu merasa artikel ini bermanfaat silakan Like & Share artikel ini ke teman-teman kamu atau jika kamu punya pertanyaan, tulis aja di kolom komentar, Thank you! 😁 🙏
Always open to new ideas 🕊️
Articles that you might want to read.

Tutorial membongkar dan memodifikasi aplikasi iOS 🍎

Cheatsheet untuk menerapkan Design System pada Material UI

Tutorial multilingual menggunakan react-i18next