Skip to main content

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:

PropTipoDescripciónUso
dataT[]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.data
isCorrectboolean | nullEste 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.
ClearDroppablesboolean | nullCada vez que este estado está en true, limpia todo el dropState dejándolo en null.
ChildrenReact.ReactNodeDentro 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.droppableNdraggable

Asi es como se deberia usar el DragAndDropProvider:

Example Drag and Drop Provider

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

Example Drag and Drop Provider

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

    Ejemplo de dropState

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 droppable al mismo tiempo, puede ocurrir una condición de carrera en la asignación de IDs, causando conflictos.

    • El estado globalDroppableId comienza 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 droppable creen que son el ID 0, afectando siempre la primera posición en DroppedItems.

    • 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ónDescripció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: numberAl 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:numberCuando 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_CORRECTSe 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 | nullSe 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