Search code examples
javascriptreactjsreact-dnd

Uncaught Invariant Violation: Expected drag drop context even after using DndProvider


I have used the DndProvider wrapper in my main file It was working properly before. But after I imported and used useDrop it started throwing the drag drop context error

/* eslint-disable no-unused-vars */
import React, { useState } from 'react'
import Sidebar from './AAComponents/AASidebar'
import DraggableItem from './AAComponents/AADraggableItem'
import DndProviderWrapper from './AADndProviderWrapper'
import { DndProvider, useDrop } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'

const AAWhatsapp = () => {
  const [draggedComponents, setDraggedComponents] = useState([])
  const [canvasSize, setCanvasSize] = useState({ width: 800, height: 600 })

  const handleSidebarItemClick = (componentType) => {
    setDraggedComponents((prev) => [...prev, { type: componentType, position: { x: 0, y: 0 } }])
  }

  const handleDrag = (index, delta) => {
    setDraggedComponents((prev) => {
      const updatedComponents = [...prev]
      updatedComponents[index].position.x += delta.x
      updatedComponents[index].position.y += delta.y

      const canvasWidth = canvasSize.width
      const canvasHeight = canvasSize.height

      if (updatedComponents[index].position.x < 0) {
        updatedComponents[index].position.x = 0
      } else if (updatedComponents[index].position.x + 100 > canvasWidth) {
        updatedComponents[index].position.x = canvasWidth - 100
      }

      if (updatedComponents[index].position.y < 0) {
        updatedComponents[index].position.y = 0
      } else if (updatedComponents[index].position.y + 50 > canvasHeight) {
        updatedComponents[index].position.y = canvasHeight - 50
      }

      return updatedComponents
    })
  }

  const handleDropFromSidebar = (item) => {
    const newItem = {
      type: item.type,
      position: { x: 100, y: 100 }
    }

    setDraggedComponents((prev) => [...prev, newItem])
  }

  const [, drop] = useDrop({
    accept: 'SIDEBAR_ITEM',
    drop: (item) => handleDropFromSidebar(item)
  })

  return (
    <DndProvider backend={HTML5Backend}>
      <div style={{ display: 'flex' }}>
        <Sidebar onItemClick={handleSidebarItemClick} />
        <div
          ref={drop}
          style={{
            flex: 1,
            padding: '20px',
            position: 'relative',
            width: canvasSize.width,
            height: canvasSize.height,
            border: '1px solid #ccc',
            overflow: 'hidden'
          }}
        >
          {draggedComponents.map((component, index) => (
            <DraggableItem key={index} index={index} component={component} onDrag={handleDrag} />
          ))}
        </div>
      </div>
    </DndProvider>
  )
}

export default AAWhatsapp

here is the rest of the code

/* eslint-disable no-unused-vars */
import React from 'react'
import SidebarItem from './AASidebarItem'

const AASidebar = ({ onItemClick }) => {
  const handleItemClick = (component) => {
    onItemClick(component)
  }


  return (
    <div className='side' style={{ width: '200px', background: '#f0f0f0', padding: '20px' }}>
    <SidebarItem type='Text' />
    <SidebarItem type='Image' />
  </div>
  )
}

export default AASidebar
import React from 'react'
import { useDrag } from 'react-dnd'

const AASidebarItem = ({ type }) => {
  const [{ isDragging }, drag] = useDrag({
    type: 'SIDEBAR_ITEM', // Set a unique type for sidebar items
    item: { type },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging()
    })
  })

  return (
    <div
      ref={drag}
      className='m-1 bg-success cursor-pointer'
      style={{ opacity: isDragging ? 0.5 : 1 }}
    >
      {type}
    </div>
  )
}

export default AASidebarItem
import React from 'react'
import { useDrag } from 'react-dnd'

const AADraggableItem = ({ component, index, onDrag }) => {
  const [{ isDragging }, drag] = useDrag({
    type: 'COMPONENT',
    item: { type: component.type, index },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging()
    }),
    end: (item, monitor) => {
      const delta = monitor.getDifferenceFromInitialOffset()
      if (delta) {
        onDrag(index, delta)
      }
    }
  })

  const getDynamicStyles = () => {
    switch (component.type) {
      case 'Text':
        return { width: '100px', height: '50px', fontSize: '16px', fontWeight: 'bold' }
      case 'Image':
        return { width: '150px', height: '100px' }
      default:
        return { width: '100px', height: '50px' }
    }
  }

  const dynamicStyles = getDynamicStyles()

  const renderItemContent = (component) => {
    switch (component.type) {
      case 'Text':
        return <span>A Text Item</span>
      case 'Image':
        return <img src="path/to/image.png" alt="Image Item" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
      default:
        return <span>{component.type}</span>
    }
  }

  return (
    <div
      ref={(node) => drag(node)}
      style={{
        width: dynamicStyles.width,
        height: dynamicStyles.height,
        background: isDragging ? '#87CEEB' : '#e0e0e0',
        position: 'absolute',
        left: component.position.x,
        top: component.position.y,
        cursor: 'move',
        opacity: isDragging ? 0 : 1,
        border: '1px solid #000',
        ...dynamicStyles 
      }}
    >
      {renderItemContent(component)}
    </div>
  )
}

export default AADraggableItem

enter image description here

I tried creating a separate DndProviderWrapper component and used it to wrap my entire application. Didn't work


Solution

  •   const [, drop] = useDrop({
    accept: 'SIDEBAR_ITEM',
    drop: (item) => handleDropFromSidebar(item)
    

    handleDropFromSidebar

    where is this defined?