Next.js Discord

Discord Forum

Property 'type' does not exist on type 'IntrinsicAttributes & DraggableProps', NEXTJS/ React

Answered
Petit Bleu de Gascogne posted this in #help-forum
Open in Discord
Petit Bleu de GascogneOP
I'm using @hello-pangea/dnd (react-beautiful-dnd fork) to create a drag and drop list.
The thing is that, I'm using this custom prop
    
<Draggable draggableId={section.id} index={index} type="section">
//type <- new/ custom prop

But, it doesn't exist on type 'IntrinsicAttributes & DraggableProps'.

Because of this error, I cannot build the app, but, what I've managed to do, is to add this into type

type DraggableChildrenFn = (provided: DraggableProvided, snapshot: DraggableStateSnapshot, rubic: DraggableRubric) => ReactNode | null;
interface DraggableProps {
    draggableId: DraggableId;
    index: number;
    children: DraggableChildrenFn;
    isDragDisabled?: boolean;
    disableInteractiveElementBlocking?: boolean;
    shouldRespectForcePress?: boolean;
    type?: string; <- this type
}


The app now builds, locally, but on Vercel, I still get that error.

What I've tried to do was, to add to github the path to the dnd types node_modules\.pnpm\@hello-pangea+dnd@16.6.0_@types+react-dom@18.2.24_@types+react@18.2.74_react-dom@18.2.0_react@18.2.0\node_modules\@hello-pangea\dnd\dist\dnd.d.ts

I've managed to add it to repo, but, on vercel, it's downloading the package again, and I assume it's overlapping it.

Another thing I've tried was to create an interface that extends the props type, but it doesn't work...

I want to keep that type prop, because, I'm using the component twice for 2 separate things, and this allows me to track where do I do modifications.

    if (type === "section") {
      handleSectionDragDrop(source, destination, updatedSections);
    } else {
      handleTaskDragDrop(source, destination, updatedSections);
    }
Answered by Rafael Almeida
it is not very clean but instead of this:
<Droppable droppableId={section.id} type="task">

you can do something like this:
<Droppable droppableId={`task|${section.id}`}>

then in your onDragEnd function you can read the type with
const type = result.droppableId.split('|')[0]
View full answer

28 Replies

Petit Bleu de GascogneOP
it's driving me crazy lol, i couldn't find any solutions.. please, anyone help me ;))
Petit Bleu de GascogneOP
up. anyone, please?
@Petit Bleu de Gascogne I'm using @hello-pangea/dnd (react-beautiful-dnd fork) to create a drag and drop list. The thing is that, I'm using this custom prop <Draggable draggableId={section.id} index={index} type="section"> //type <- new/ custom prop But, it doesn't exist on type 'IntrinsicAttributes & DraggableProps'. Because of this error, I cannot build the app, but, what I've managed to do, is to add this into type type DraggableChildrenFn = (provided: DraggableProvided, snapshot: DraggableStateSnapshot, rubic: DraggableRubric) => ReactNode | null; interface DraggableProps { draggableId: DraggableId; index: number; children: DraggableChildrenFn; isDragDisabled?: boolean; disableInteractiveElementBlocking?: boolean; shouldRespectForcePress?: boolean; type?: string; <- this type } The app now builds, locally, but on Vercel, I still get that error. What I've tried to do was, to add to github the path to the dnd types ` node_modules\.pnpm\@hello-pangea+dnd@16.6.0_@types+react-dom@18.2.24_@types+react@18.2.74_react-dom@18.2.0_react@18.2.0\node_modules\@hello-pangea\dnd\dist\dnd.d.ts ` I've managed to add it to repo, but, on vercel, it's downloading the package again, and I assume it's overlapping it. Another thing I've tried was to create an interface that extends the props type, but it doesn't work... I want to keep that type prop, because, I'm using the component twice for 2 separate things, and this allows me to track where do I do modifications. if (type === "section") { handleSectionDragDrop(source, destination, updatedSections); } else { handleTaskDragDrop(source, destination, updatedSections); }
yeah you can't edit a node_modules package directly because you don't share the code between machines, you re-download everything so your change won't be there

I am not sure I understand how you are using this type prop. you can't change the code of the component provided by the library so can you share a little more context about what you are trying to do? is this conditional code on type inside your own component?
Petit Bleu de GascogneOP
So, I'm using the Draggable component Twice. One for "sections" one for "items". The sections can be reordered between them, items can be reordered between them and between sections. This is the code I'm using, to track what I'm moving, and where:

const onDragEnd = (result: DropResult) => {
    const { source, destination, type } = result;

    if (!destination) return;

    const updatedSections = [...sections];

    if (type === "section") {
      handleSectionDragDrop(source, destination, updatedSections);
    } else {
      handleTaskDragDrop(source, destination, updatedSections);
    }
  };
are you also modifying the code of the Draggable component inside the node_modules folder to add the funcionality to this type prop? 🤔
Petit Bleu de GascogneOP
well, not really. I'm not building the app now, and on dev, works fine, even tho I have errors. I modified package, yes, to test if the project builds, and it worked..
But after testing it out, I deleted the modification
yeah you will need to think about a alternative solution that does not involve modifying their package. technically you can patch the package code but it is a very brittle solution that can break any time they decide to update the code
Petit Bleu de GascogneOP
This is the entire code.. I couldn't do something better for now, to check the type of item I'm moving...

  const onDragEnd = (result: DropResult) => {
    const { source, destination, type } = result;

    if (!destination) return;

    const updatedSections = [...sections];

    if (type === "section") {
      handleSectionDragDrop(source, destination, updatedSections);
    } else {
      handleTaskDragDrop(source, destination, updatedSections);
    }
  };

  const handleSectionDragDrop = (
    source: any,
    destination: any,
    updatedSections: SectionType[]
  ) => {
    const [removed] = updatedSections.splice(source.index, 1);
    updatedSections.splice(destination.index, 0, removed);
    const updatedWithPosition = updatedSections.map((section, index) => ({
      ...section,
      position: index,
    }));
    setSections(updatedWithPosition);
    updateSectionsOnServer(updatedWithPosition);
  };

  
const handleTaskDragDrop = (
    source: any,
    destination: any,
    updatedSections: SectionType[]
  ) => {
    const sourceIndex = updatedSections.findIndex(
      (section) => section.id === source.droppableId
    );
    const destIndex = updatedSections.findIndex(
      (section) => section.id === destination.droppableId
    );

    if (source.droppableId === destination.droppableId) {
      const updatedTasks = Array.from(updatedSections[sourceIndex].tasks);
      const [removed] = updatedTasks.splice(source.index, 1);
      updatedTasks.splice(destination.index, 0, removed);
      updatedSections[sourceIndex].tasks = updatedTasks;
      const updatedWithPosition = updatedTasks.map((task, index) => ({
        ...task,
        position: index,
      }));
      setSections(updatedSections);
      updateTaskOrder(updatedWithPosition);
    } else {
      const sourceTasks = Array.from(updatedSections[sourceIndex].tasks);
      const destTasks = Array.from(updatedSections[destIndex].tasks);
      const [removed] = sourceTasks.splice(source.index, 1);
      destTasks.splice(destination.index, 0, removed);
      updatedSections[sourceIndex].tasks = sourceTasks;
      updatedSections[destIndex].tasks = destTasks;
      setSections(updatedSections);
      updateTaskSection(destTasks, source.droppableId, destination.droppableId);
    }
  };
I mean, it works fine tho, but the app can't be built because of that error. I might try to get a workaround without modifying the package
where do you use this onDragEnd function?
Petit Bleu de GascogneOP
<DragDropContext onDragEnd={onDragEnd}>
            <Droppable
              droppableId="sections"
              direction="horizontal"
              type="section"
            >
              {(provided) => (
                <div
                  {...provided.droppableProps}
                  ref={provided.innerRef}
                  className="flex"
                >
                  {sections.map((section, index) => (
                    <Section key={section.id} section={section} index={index} />
                  ))}
                  {provided.placeholder}
                </div>
              )}
            </Droppable>
and, on section component

<Draggable draggableId={section.id} index={index} type="section">
      {(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => (
        <div
          ref={provided.innerRef}
          {...provided.draggableProps}
          className={`m-1 min-w-xl w-72 hover:bg-gray-100/75   dark:hover:bg-neutral-700/25 transition-colors rounded-md ${
            snapshot.isDragging ? "bg-gray-200/75 dark:bg-neutral-900/25" : ""
          }`}
        >
          <div className="">
            <div className="text-lg font-normal" {...provided.dragHandleProps}>
              <div
                className={`flex items-center justify-between gap-2 px-4 py-2 rounded-md transition-colors  ${
                  !snapshot.isDragging
                    ? "hover:bg-gray-300/50 dark:hover:bg-neutral-800/50"
                    : ""
                }`}
              >
                <div className="flex items-center gap-2">
                  <div className="size-4 bg-red-600 rounded-full" />
                  {section.title}
                </div>
              </div>
            </div>
            
            <Droppable droppableId={section.id} type="task">
              {(provided: DroppableProvided, snapshot) => (
                <div
                  {...provided.droppableProps}
                  ref={provided.innerRef}
                  className={` ${
                    snapshot.isDraggingOver ? "bg-neutral-100/25" : ""
                  }`}
                >
                  {section.tasks.map((task, index) => (
                    <Task key={task.id} task={task} index={index} />
                  ))}
                  {provided.placeholder}
                </div>
              )}
            </Droppable>
          </div>
        </div>
      )}
    </Draggable>
then, i'm passing a type again, as you can see, here

<Droppable droppableId={section.id} type="task">
              {(provided: DroppableProvided, snapshot) => (
                <div
                  {...provided.droppableProps}
                  ref={provided.innerRef}
                  className={` ${
                    snapshot.isDraggingOver ? "bg-neutral-100/25" : ""
                  }`}
                >
                  {section.tasks.map((task, index) => (
                    <Task key={task.id} task={task} index={index} />
                  ))}
                  {provided.placeholder}
                </div>
              )}
            </Droppable>
so you only set the onDragEnd once but you have multiple Draggable components nested with different types 🤔
Petit Bleu de GascogneOP
Yeah, because I have DragDropContext only once
Now that you say this, I might modify it, to have it twice.. one for sections, one for items/ tasks
Well, I'm going to try again to find a workaround for what i have now, and if nothing works, might try to create dragdropcontext twice
it doesn't look like a trivial problem actually :ThinkDerp:
I read a bit of the documentation of the library and the only solution that I thought would be using the droppableId as a way to recognize the origin of the drag event
it is not very clean but instead of this:
<Droppable droppableId={section.id} type="task">

you can do something like this:
<Droppable droppableId={`task|${section.id}`}>

then in your onDragEnd function you can read the type with
const type = result.droppableId.split('|')[0]
Answer
Petit Bleu de GascogneOP
The docs for react-beautiful-dnd is pretty vague.. they provide a type for Droppable, but not for Draggable...
Petit Bleu de GascogneOP
well, that's interesting.. I'm missing something out.. I removed the types from Draggable, and logged the onDragEnd, the type
  const onDragEnd = (result: DropResult) => {
    const { source, destination, type } = result;

    if (!destination) return;

    const updatedSections = [...sections];
    console.log(type);
};


And, apparently, it logs the correct type...
So, my guess is that, the Draggable type prop was useless, and it's getting that prop from Droppable (since there, type can be passed as a prop)
@Petit Bleu de Gascogne So, my guess is that, the Draggable `type` prop was useless, and it's getting that prop from Droppable (since there, `type` can be passed as a prop)
this dnd library uses the type prop to limit where you can drop stuff, it makes sense that it is not available for Draggable:

you have a Droppable component where you have a list of users, so you set the type to USERS. then on the other list of users you also set it to USERS

now you have a third Droppable component for idk, fruits. you don't want to let the user drop users on the list of fruits so for this Droppable you set the type to FRUITS

the type of the Draggable always inherits from the Droppable so you don't mix different types of items in the wrong list
Petit Bleu de GascogneOP
It works. It deploys to vercel successfully. Thank you!!!