Artigo original: React.js: implement the drag and drop feature without using external libraries
Aprenda os detalhes da implementação do recurso de arrastar e soltar (em inglês, drag and drop) no React do zero.
Vamos primeiro ver o resultado do que vamos construir. Estou testando o .gif — espero que funcione em todos os lugares como esperado. Usei para criá-lo o Camtasia com uma licença pessoal.

Os principais pontos de aprendizagem são:
- Tornar um elemento "arrastável" adicionando o atributo "draggable"
- Tornar uma área "soltável" implementando o evento "dragover"
- Capturar os dados para arrastar implementando o evento "dragstart"
- Capturar o momento de soltar implementando o evento "drop"
- Implementar o evento "drag" que é acionado enquanto o elemento está sendo arrastado
- Armazenar os dados intermediários no objeto dataTransfer
Passo 1 — crie o aplicativo raiz para a demonstração
Todo o código para arrastar e soltar estarrá no componente AppDragDropDemo.js
.
import React from 'react';import ReactDOM from 'react-dom';import '.index.css';import AppDr
ReactDOM.render(<AppDragDropDemo />, document.getElementById("root"));
O ponto de entrada para o AppDragDropDemo
tem a seguinte aparência no código:
import React, { Component } from 'react';
export default class AppDragDropDemo extends Component {
render () {
return (<div className="container-drag">DRAG & DROP DEMO</div>);
}
}
Se você executar agora a aplicação, você verá esta tela:

Passo 2 — crie o estado do objeto para armazenar algumas tarefas
Vamos criar algumas tarefas para simular uma aplicação simples. O que pretendemos fazer é arrastar e soltar essas tarefas em diferentes categorias como wip
, complete
e assim por diante.
export default class AppDragDropDemo extends Component {
state = {
tasks: [{
name:"Learn Angular",
category:"wip",
bgcolor: "yellow"
},
{
name:"React",
category:"wip",
bgcolor:"pink"
},
{
name:"Vue",
category:"complete",
bgcolor:"skyblue"
}
]
}
render () {
return (<div className="container-drag">DRAG & DROP DEMO</div>);}
}
}
Passo 3 — organize os dados em categorias
Vamos implementar o código abaixo no método render
para agrupar as tarefas em suas respectivas categorias, wip
e complete
. Sinta-se à vontade para adicionar mais categorias e brincar com o código.

Você pode copiar e colar o código que aparece acima no código abaixo.
render() {
var tasks = {
wip: [],
complete: []
}
this.state.tasks.forEach ((t) => {
tasks[t.category].push(<div key={t.name) onDragStart={(e)=>this.onDragStart(e, t.name)} draggable className="draggable" style={{backgroundColor: t.bgcolor}}>{t.name}</div>);
}
};
No código acima, estamos percorrendo todas as tarefas, criando uma div
para cada item de tarefa e armazenando-o nas respectivas categorias.
Assim, o wip[]
contém todas as tarefas na categoria wip
e complete[]
contém todas as tarefas concluídas.
Passo 4 — torne o item da tarefa arrastável
Adicione o atributo arrastável a <div>
ou a qualquer elemento para tornar um elemento arrastável. Consulte o bloco de código abaixo para obter o formato de texto do código.

Passo 5 — crie um contêiner "soltável"
Para criar um contêiner "soltável", implemente o evento dragover
. Agora, como queremos desabilitar o padrão (default) do evento dragover, vamos chamar event.preventDefault()
nele.
Também renderizaremos {tasks.wip}
e {tasks.complete}
em seus elementos div
correspondentes.

return (<div className="container-drag">
<h2 className="header">DRAG & DROP DEMO</h2>
<div className="wip" onDragOver={(e)=>this.onDragOver(e)} onDrop={(e)=>{this.onDrop(e, "wip")}}>
<span className="task-header">WIP</span>
{tasks.wip}
</div>
<div className="droppable" onDragOver={(e)=>this.onDragOver(e)} onDrop={(e)=>this.onDrop(e, "complete")}>
<span className="task-header">COMPLETED</span>
{tasks.complete}
</div>
</div>);

Vamos, agora, implementar o manipulador de eventos onDragOver()
.
A resultado até agora será semelhante à figura abaixo.

Passo 6 — capture o estado do elemento que está sendo arrastado
Vamos modificar o código onde estamos criando a categoria para cada tarefa. Adicione um manipulador de evento ondragstart
e passe o id/nome ou qualquer informação que você precise persistir enquanto o arrastar/soltar está acontecendo.
Estou usando name
como um valor único para identificar a tarefa. Sinta-se à vontade para usar o ID ou qualquer chave exclusiva que você tenha.

Vamos agora implementar o manipulador de eventos onDragStart
.

No manipulador onDragStart
, pegamos o parâmetro e o armazenamos no objeto dataTransfer
. Não se confunda com a nomenclatura do parâmetro, pois acho que estava em outro mundo quando codifiquei isso. :)
Observação para o Internet Explorer: isso pode não funcionar com o IE. Para ele, a melhor prática é fornecer o formato como a chave, conforme mostrado abaixo.
Em vez de
ev.dataTransfer.setData("id", id)
use
ev.dataTransfer.setData(“text/plain”,id)
O manipulador acima garantirá que o elemento que está sendo arrastado seja armazenado no evento do objeto e esteja disponível para uso quando necessário. Pode ser necessário ao soltar em um destino.
Agora, se você executar o aplicativo e arrastar os elementos, os seguintes logs serão gerados.

Passo 7 — manipule o evento de soltar (drop)
Vamos abrir o método de renderizar e adicionar o evento onDrop
na div
com a className
droppable
.

No código acima, adicionamos o evento de manipulação drop
e passamos a categoria obrigatória complete
como argumento. Isso indica que estamos "soltando" o elemento do estado wip
no estado complete
(categoria). Sinta-se à vontade para alterar os nomes, conforme necessário.
Vamos agora implementar o manipulador de eventos onDrop()
.

Aqui está o código que você pode copiar/colar:
onDrop = (ev, cat) => {
let id = ev.dataTransfer.getData("id");
let tasks = this.state.tasks.filter((task) => {
if (task.name == id) {
task.category = cat;
}
return task;
});
this.setState({
...this.state,
tasks
});
}
No manipulador de eventos onDrop
, pegamos a tarefa que está sendo arrastada usando o método getData
no evento do objeto dataTransfer
.
Em seguida, criamos um array de tarefas usando o método de filtro e alteramos a categoria da tarefa que está sendo arrastada.
setState()
acionará a renderização e as tarefas serão renderizadas nas áreas corretas.
Observação para o Internet Explorer: para fazê-lo funcionar no IE, use o método getData
abaixo.
Ao invés de
var id = ev.dataTransfer.getData("id")
use
var id = ev.dataTransfer.getData("text")
Passo 8 — para implementar o soltar (drop) de "complete" para "wip", adicione o manipulador onDropto
O manipulador onDrop()
permanece o mesmo de antes.

Por fim, execute o código, admire sua criação e divirta-se enquanto programa. :)
Você pode pegar o código-fonte aqui.
Observação: para que isso funcione em vários navegadores, altere o tipo de setData
para string. Por exemplo, para definir dados, use ev.dataTransfer.setData("text/plain",id)
. Para ler o dado, use var id = ev.dataTransfer.getData("text")
.
Como meu objetivo era demonstrar os principais recursos de arrastar e soltar, o código não foi otimizado para fatores como design e convenções de nomenclatura.
Aprenda com o autor em @Learner + Fullstack Coach (@rajeshpillai): https://twitter.com/rajeshpillai