@dnd-kit weird sorting behavior when swapping draggable with more than 1 element from the list

182 Views Asked by At

I am using dnd-kit and facing an issue! When I swap draggable with any first element from the list (either above or below) it works fine, but IF I CONTINUE dragging and swapping with the following elements, the sorting behaves strangely.

Let me show you the GIF: Link

Here's the code for the list item:

import React, { useState } from "react";
import "./FieldTokenComponent.scss";
import { TokenData } from "../../../classes/TokenClasses/TokenCreator";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";

export interface FieldTokenData {
  tokenData: TokenData;
}

export default function FieldTokenComponent(props: FieldTokenData) {

  const { tokenData } = props;

  const {
    tokenLabelColor,
    tokenAlias,
    tokenDefense,
    tokenHP,
    tokenInitiative,
    tokenSpeed,
    tokenType,
    id,
  } = tokenData;

  const { attributes, listeners, setNodeRef, transform, transition } =
    useSortable({ id: id });

  const style = {
    transition,
    transform: CSS.Transform.toString(transform),
    backgroundColor: `${tokenEliminated ? "gray" : tokenLabelColor}`,
  };

  return (
    <div>
      <div
        id="battlefieldToken"
        data-type={tokenType}
        style={style}
        ref={setNodeRef}
        {...attributes}
        {...listeners}
      >
        <span className="tokenInitiative">{tokenInitiative}</span>
          <i className={`fa-solid fa-skull deathStatusIcon`}></i>
        </span>
        <span className="tokenName">{tokenAlias}</span>
        <button onClick={expandToken} className="tokenExpandButton">
          <i className="fa-solid fa-angle-down expandArrow"></i>
        </button>
      </div>
    </div>
  );
}

Here's the code for the list container:

import "./BattlefieldComponent.scss";
import TokenModal from "../../UIComponents/AddTokenModal/TokenModal";
import { useState } from "react";
import { DndContext, DragEndEvent } from "@dnd-kit/core";
import { closestCenter } from "@dnd-kit/core";
import { TokenData } from "../../../classes/TokenClasses/TokenCreator";
import FieldTokenComponent from "../../TokenComponents/FieldTokenComponent/FieldTokenComponent";
import {
  SortableContext,
  arrayMove,
  verticalListSortingStrategy,
} from "@dnd-kit/sortable";

export function BattlefieldComponent() {

  const [creaturesTokens, setCreaturesToken] = useState<
    { data: TokenData; id: number }[]
  >([]);

  function onDragEnd(event: DragEndEvent) {
    const { active, over } = event;
    console.log(event);
    if (active.id !== over!.id) {
      setCreaturesToken((tokens) => {
        const newIndex = tokens.findIndex((token) => token.id === active.id);
        const oldIndex = tokens.findIndex((token) => token.id === over!.id);
        return arrayMove(tokens, oldIndex, newIndex);
      });
    }
  }

  return (
    <div className="battlefieldZone">
      <div className="battlefieldCol" id="creaturesCol">
        <header>
          <h3>CREATURES</h3>
        </header>

        <main className="battlefieldRow">
          <DndContext collisionDetection={closestCenter} onDragEnd={onDragEnd}>
            <SortableContext
              items={creaturesTokens}
              strategy={verticalListSortingStrategy}
            >
              {creaturesTokens.map((tokenData) => (
                <FieldTokenComponent
                  tokenData={tokenData.data}
                  key={tokenData.data.tokenId}
                />
              ))}
            </SortableContext>
          </DndContext>
        </main>

        <TokenModal
          tokenType="creaturesToken"
          addToken={setCreaturesToken}
          key="creaturesTokenModal"
        />
      </div>
    </div>
  );
}

TokenModal is with what I add draggable elements, I am not sure if I need to add this code here, so please, tell me if you think it is relevant.

1

There are 1 best solutions below

0
Werthis On

You should use the same id as key in FieldTokenComponent and in useSortable({ id: id })

Parent:

<FieldTokenComponent
  tokenData={tokenData.data}
  key={tokenData.data.tokenId}
/>

Child:

const { attributes, listeners, setNodeRef, transform, transition } =
    useSortable({ id: id });