dnd-kit icon indicating copy to clipboard operation
dnd-kit copied to clipboard

'Maximum update depth exceeded' error with Sortable

Open giovannipds opened this issue 2 years ago • 14 comments

I'm just reopening this issue https://github.com/clauderic/dnd-kit/issues/496, people has been posting there, so prolly the issue is still around (I have that in the project that I'm working on it too lol).

giovannipds avatar Sep 30 '22 14:09 giovannipds

Has there been any update on this issue?

RHarfordKitmanLabs avatar Nov 01 '22 12:11 RHarfordKitmanLabs

also got this issue, mine fine in firefox, but fail in chrome

hermantolim avatar Jun 05 '23 09:06 hermantolim

Edit: I found the solution. In onDragOver, any state update you make, wrap it inside startTransition or requestAnimationFrame. startTransition is native way by react though

So, here's my demo. In my case, it's happening because of mixed height items. When all items are of equal height, there's no error.

[

https://github.com/clauderic/dnd-kit/assets/13780952/3dd5e5b3-901f-4344-9928-9c35cc9c114d

](url)

eashish93 avatar Dec 07 '23 09:12 eashish93

same issue +1

phongnguyenhh1996 avatar Dec 23 '23 13:12 phongnguyenhh1996

I had the same problem and I fixed it by using a composition of existing collision-detection algorithms:

import { closestCorners, closestCenter, pointerWithin } from "@dnd-kit/core";

export function customCollisionDetectionAlgorithm(args) {
    const closestCornersCollisions = closestCorners(args);
    const closestCenterCollisions = closestCenter(args);
    const pointerWithinCollisions = pointerWithin(args);

    if (
        closestCornersCollisions.length > 0 &&
        closestCenterCollisions.length > 0 &&
        pointerWithinCollisions.length > 0
    ) {
        return pointerWithinCollisions;
    }

    return null;
}

And then of course used it in the DndContext:

<DndContext
    onDragStart={onDragStart}
    onDragEnd={onDragEnd}
    onDragOver={onDragOver}
    sensors={sensors}
    collisionDetection={customCollisionDetectionAlgorithm}
>

NoamBasha avatar Jan 06 '24 23:01 NoamBasha

I am getting the same issue, I have tried the suggestions above but no joy.

Here is my code:

const SortableList = ({ items, onSortEnd, hasDragHandle }: SortableListProps) => {
    const [activeId, setActiveId] = useState<UniqueIdentifier | null>(null);
    const getIndex = (id: UniqueIdentifier) => items.indexOf(+id);
    const sensors = useSensors(
      useSensor(MouseSensor, {
        // Require the mouse to move by 10 pixels before activating.
        // Slight distance prevents sortable logic messing with
        // interactive elements in the handler toolbar component.
        activationConstraint: {
          distance: 10,
        },
      }),
      useSensor(TouchSensor, {
        // Press delay of 250ms, with tolerance of 5px of movement.
        activationConstraint: {
          delay: 250,
          tolerance: 5,
        },
      }),
    );

    const previewItems = useMemo(
      () => items.map((item) => previewedResultsGrid[item.index]),
      [items],
    );

    return (
      <DndContext
        sensors={sensors}
        autoScroll={false}
        onDragStart={({ active }) => {
          if (active) {
            setActiveId(active.id);
          }
        }}
        onDragEnd={({ active, over }) => {
          if (over && active.id !== over.id) {
            onSortEnd({
              oldIndex: getIndex(active.id),
              newIndex: getIndex(over.id),
            });
          }
          setActiveId(null);
        }}
        onDragCancel={() => setActiveId(null)}
      >
        <SortableContext items={previewItems} strategy={rectSwappingStrategy}>
          <div
            ref={parentRef}
            className={cx(
              stl`relative select-none overflow-x-hidden overflow-y-scroll`,
              'NewRuleResult_container',
              currentRule.hasConditions && currentRule.hasConsequences && 'with-confirmBar',
            )}
            style={{
              ...containerStyle,
              width: layout.width,
              height: layout.height,
              paddingBottom: currentRule.hasConditions && currentRule.hasConsequences ? 85 : 0,
            }}
          >
            <ul
              className={stl`relative overflow-hidden`}
              style={{
                height: totalSize + DETAILS_EXPANSION_HEIGHT,
                width: layout.width - containerLeftPadding - containerRightPadding,
              }}
            >
              {virtualItems.map((item) => {
                const columnWidth = layout.itemWidth + layout.gutterSize;
                const sharedItemStyles: CSSProperties = {
                  position: 'absolute',
                  top: item.start,
                  height: rowHeight,
                  ...(layout.mode === 'grid'
                    ? {
                        width: columnWidth,
                        paddingTop: MULTISELECT_CHECKBOX_SIZE / 2,
                      }
                    : { width: layout.itemWidth, padding: MULTISELECT_CHECKBOX_SIZE / 2 }),
                };

                return (
                  <Fragment key={`${layout.mode}-${item.index}`}>
                    {previewedResultsGrid[item.index].map((result, columnIndex) => {
                      return (
                        <li
                          key={result.objectID}
                          style={{ ...sharedItemStyles, left: columnWidth * columnIndex }}
                        >
                          hello
                        </li>
                      );
                    })}
                    ;
                  </Fragment>
                );
              })}
              ;
            </ul>
          </div>
        </SortableContext>
      </DndContext>
    );
  };

  return <SortableList items={virtualItems} onSortEnd={onSortEnd} hasDragHandle={true} />;
};

export const SortableResultsItems = SortableContainer(observer(ResultsItems));

thomasbritton avatar Jan 15 '24 17:01 thomasbritton

@thomasbritton Try following:

...
onDragStart={({ active }) => {
  if (active) {
    setTimeout(() => setActiveId(active.id), 0)
  }
}}
...

HenrikZabel avatar Feb 11 '24 21:02 HenrikZabel

the solution by @HenrikZabel works. However, I implemented it in OnDragOver to update the array at a timeout of 5 ms. the issue is clearly happening with ondragover.

bhutoria avatar Mar 19 '24 16:03 bhutoria

I had the same issue although I did not use onDragOver. For me the problem was that i used the PointerSensor instead of the MouseSensor

const sensors = useSensors(useSensor(MouseSensor)); <----- This fixed it

<DndContext sensors={sensors} onDragEnd={handleDragEnd} collisionDetection={closestCenter}>
     <SortableContext items={items} strategy={rectSwappingStrategy}>
            {...children}
     </SortableContext>
</DndContext>

So changing the Sensor fixed it for me.

Maybe this helps someone having the same issue.

Dentling avatar Apr 04 '24 16:04 Dentling

I had the same problem and I fixed it by using a composition of existing collision-detection algorithms:

import { closestCorners, closestCenter, pointerWithin } from "@dnd-kit/core";

export function customCollisionDetectionAlgorithm(args) {
    const closestCornersCollisions = closestCorners(args);
    const closestCenterCollisions = closestCenter(args);
    const pointerWithinCollisions = pointerWithin(args);

    if (
        closestCornersCollisions.length > 0 &&
        closestCenterCollisions.length > 0 &&
        pointerWithinCollisions.length > 0
    ) {
        return pointerWithinCollisions;
    }

    return null;
}

And then of course used it in the DndContext:

<DndContext
    onDragStart={onDragStart}
    onDragEnd={onDragEnd}
    onDragOver={onDragOver}
    sensors={sensors}
    collisionDetection={customCollisionDetectionAlgorithm}
>

This also seems to fix the same issue I was facing. Thank you.

Lekanjoy avatar Apr 22 '24 01:04 Lekanjoy

So I thought the custom collision detection was fixing it, but it turns out it was not. I instead implemented a 0ms debounced state setter based on this comment and this is now working perfectly.

import {
    DndContext,
    DragOverlay,
    MouseSensor,
    TouchSensor,
    useSensor,
    useSensors,
    type DragEndEvent,
    type DragOverEvent,
    type DragStartEvent
} from "@dnd-kit/core";

import { useDebouncedCallback } from "use-debounce";
// ...

export function Kanban() {
    // ...
    const [items, setItemsUndebounced] = useState<Item[]>(initialItems);
    const setItems = useDebouncedCallback(setItemsUndebounced, 0);
    // ...
    function onDragOver(event: DragOverEvent) {
        // ...
            setItems((items) => {
                // ...
                return arrayMove(items, activeIndex, overIndex);
            });
        // ...
    }
    // ...
}

timreach avatar May 15 '24 14:05 timreach

Edit: I found the solution. In onDragOver, any state update you make, wrap it inside startTransition or requestAnimationFrame. startTransition is native way by react though

So, here's my demo. In my case, it's happening because of mixed height items. When all items are of equal height, there's no error.

[

Screen.Recording.2023-12-07.at.3.25.11.PM.mov ](url)

thank you so much your solution helped me

AmirhoseinHesami avatar Jun 02 '24 08:06 AmirhoseinHesami

I had the same error, i was using the Component with useDroppable() hook within the same component that uses it, i just separated the component with useDroppable() hook into a separate file and imported into the main component. Now it works! Hope it would help someone.

rahulthomasdev avatar Jun 04 '24 09:06 rahulthomasdev