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 AppDrReactDOM.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