DragAndDropProvider
DragAndDropProvider es un componente de explorax que maneja estados de drag and drop usando React context y React-DnD. Deja que los componentes hijos interactuen con estados que ayudan con el manejo del drag and drop. Es un componente que tiene que estar obligatorio para que los componentes Draggable y Droppable funcionen.
Props:
| Prop | Tipo | Descripción | Uso |
|---|---|---|---|
data | T[] | Este prop es opcional y por defecto es "[]". Recibe un array de objetos necesario para guardar toda la información que va a ser desplegada en el drag and drop. | |
isCorrect | boolean | null | Este prop es opcional y por defecto es "null". Sirve para saber cuando la respuesta está correcta o incorrecta, permitiendo que los draggable y droppable lo sepan también. | |
ClearDroppables | boolean | null | Cada vez que este estado está en true, limpia todo el dropState dejándolo en null. | |
Children | React.ReactNode | Dentro de este prop van todos los componentes de drag and drop. Además, obligatoriamente tiene que recibir un prop llamado dropState para saber qué está pasando en el drag and drop. |
Asi es como se deberia usar el DragAndDropProvider:

Como se muestra en el ejemplo anterior se ve de la siguiente manera:

Estados
Este es un manejador de estados con los siguientes estados clave:
1. DropState
Guarda la información de los elementos arrastrados y su estado en los elementos droppable:
-
DroppedItems:- Es un array generado automáticamente que refleja los droppables disponibles.
- Al inicio, si existen dos droppables, el array se verá así:
[null, null]. - Si se arrastra un objeto a la posición 1, el array cambia a
[null, { element_Drop }].
-
currentDroppedItem:- Mantiene el último objeto que fue dropeado, sin importar en qué droppable se colocó.

2. data
Guarda la información enviada a través del prop, accesible tanto para el draggable como para el droppable.
3. globalDroppableId
Almacena un ID único para cada droppable creado, permitiendo su comunicación con DroppedItems.
-
Problema: Race condition en la Asignación de IDs
-
Cuando se crean múltiples instancias del componente
droppableal mismo tiempo, puede ocurrir una condición de carrera en la asignación de IDs, causando conflictos. -
El estado
globalDroppableIdcomienza en 0, lo que permite que varias instancias accedan a este valor simultáneamente, provocando asignaciones incorrectas.Solución: Especificar manualmente los IDs usando la propiedad
id. -
Ejemplo de mal funcionamiento
<>
<Droppable quantity={1} />
<Droppable quantity={1} />
</>En este caso, ambos
droppablecreen que son el ID 0, afectando siempre la primera posición enDroppedItems. -
Ejemplo de asignación manual de IDs
<>
<Droppable id={0} quantity={1} />
<Droppable id={1} quantity={1} />
</> -
Ejemplo avanzado de asignación de IDs
<>
<Droppable quantity={3} />
<Droppable id={3} quantity={1} />
</>
-
4. isCorrectAnswer
Guarda el estado isCorrect para que tanto el draggable como el droppable puedan acceder a él.
5. draggablesConfigurations
Mantiene un array con todas las configuraciones de los draggables. Cada posición del array corresponde a una configuración específica, permitiendo que los droppables accedan a ellas de acuerdo a su índice.
Funciones de dispatch:
| Tipo de Acción | Descripción |
|---|---|
SET_DATA; Payload: T[] | Esta función se usa únicamente la primera vez que se crea el componente, guarda en los estados la información del prop data. |
INCREMENT_GLOBAL_DROPPABLE_ID; increment: number | Al crear un componente droppable, llama a esta función para incrementar el globalDroppableId cierta cantidad. Por defecto, incrementa en 5 y ajusta el tamaño de DroppedItems en dropState. |
ADD_DRAGGABLE_CONFIG: payload: DraggableConfig<T> | Se llama al crear un elemento Draggable, agregando su configuración al estado draggablesConfigurations. |
SET_DROPSTATE_DROPPEDITEMS; payload: T; index:number | Cuando un objeto Draggable es tirado en un Droppable, actualiza o reemplaza un elemento del estado dropState, colocando la información en DroppedItems y actualiza currentDroppedItem. |
SET_IS_CORRECT | Se llama cada vez que isCorrect cambia de valor, actualizando el estado isCorrect para que todos los elementos del drag and drop estén enterados de este cambio. |
CLEAR_DROPPABLE; payload: boolean | null | Se llama cada vez que se quiere hacer un reset del estado dropState. |
NOTAS
Al renderizar re rendizar tanto los estados el drag and drop dentro de el solo puede aceptar un useEffect asi que usa este useEffect para sacar la informacion que te sea necesaria.
<DragnDropProvider>
{({ dropState }) => {
useEffect(() => {
/**
* Siempre utiliza esta validacion porque al crearse el componente
* puede que por un segundo el estado este vacio
* y se dispare este useEffect sin que tu lo tuvieras en cuenta
* **/
if(dropState.DroppedItems.lenght == 0) return
//La informacion que desees sacar
...
},[dropState.DroppedItems])
return (...)
}}
</DragnDropProvider>
TIP: Para interactuar con la información del drag and drop, intenta hacerlo fuera del contexto del dropState. Utiliza el dropState únicamente para obtener la información necesaria.
const TestComponent => (...props) => {
const [currentItem, setCurrenItem] = useState<object | null>(null)
return (
<DragnDropProvider>
{({ dropState }) => {
useEffect(() => {
if(dropState.currentDroppedItem === null) return
setCurrenItem(dropState.currentDroppedItem)
...
},[dropState.currentDroppedItem])
return (...)
}}
</DragnDropProvider>
)
}
Usando dropState
dropState siempre te va a regresar la informacion que fue droppeada junto con un droppableID que te indica en cual fue droppeado.
const testData = [
{
value: "1",
personaje: "chanin"
},
{
value: "2",
chanin: "chanina"
},
]
const TestComponent => (...props) => {
const [currentItem, setCurrenItem] = useState<object | null>(null)
useEffect(() => {
console.logs(currentItem)
/**
*
* Esto Imprimira
* {
* droppableId: 0
* value: "1"
* personaje: "chanina"
*}
*
* **/
},[currentItem])
return (
<DragnDropProvider data={testData}>
{({ dropState }) => {
useEffect(() => {
if(dropState.currentDroppedItem === null) return
/**
* El objeto que acaba de ser tirado es
* {
value: "2",
chanin: "chanina"
},
*/
setCurrenItem(dropState.currentDroppedItem)
...
},[dropState.currentDroppedItem])
return (...)
}}
</DragnDropProvider>
)
}
Donde se encuentra
El uso de este componente se puede encontrar en cualquier archivo de DnDTemplates que esta en @components/DnDTemplates