You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React DnD-Kit拖拽时元素不可见问题排查

React DnD-Kit拖拽异常原因分析

我用React DnD-Kit实现拖拽功能,参考官方Sortable示例修改。最初用useSortable({id: customer.id})时,拖拽有动画但释放后元素位置重置;改成useSortable({id: customer})后,释放能更新列表,但拖拽过程中元素不可见。请问这一现象的原因是什么?


完整代码

SortableCustomerRecord组件

import React from 'react';
import {useSortable} from '@dnd-kit/sortable';
import {CSS} from '@dnd-kit/utilities';

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

const style = {
transform: CSS.Transform.toString(transform),
transition,
};

return (
  <div ref={setNodeRef} style={style} {...attributes} {...listeners}>
    <p>{customer.name}</p>
  </div>
);
}

RoundCustomerList组件

import axios from "axios";
//React
import React, { useRef, useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
//Icons
import { faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
//Gloabal auth via user
import { useAuthContext } from "../../../Hooks/useAuthContext";
//Components
import Spinner from "../../../Components/Loading/Spinner";
import {SortableCustomerRecord} from "./SortableCustomerRecord";
import {
  DndContext, 
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
} from '@dnd-kit/core';
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable';

export default function RoundCustomerList({
  selectedRound,
  roundCustomers,
  setRoundCustomers,
  setSelectedCustomer,
  customersLoading,
}) {
console.log(roundCustomers)

  const [items, setItems] = useState(roundCustomers);
  const { user } = useAuthContext();

  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  );
  
  function handleDragEnd(event) {
    const {active, over} = event;
    
    if (active.id !== over.id) {
      setItems((items) => {
        const oldIndex = items.indexOf(active.id);
        const newIndex = items.indexOf(over.id);
        return arrayMove(items, oldIndex, newIndex);
      });
    }
  }
  
  useEffect(() => {
    console.log("list changed")
    const data = {
      roundCustomers: [],
    };
    items.forEach(function(customer, index){ 
      data.roundCustomers.push({
        customerId: customer.customerId,
        roundId: selectedRound,
        position: index,
      });
    });
    axios
      .post(
        `https://${import.meta.env.VITE_SERVER_URL}/api/round/${selectedRound}/customers/`,
        data,
        {
          headers: { Authorization: `bearer ${user.token}` },
        }
      )
  }, [items]);

  return (
   <div className="mb-3" style={{ height: "100%" }}>
    <DndContext 
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragEnd={handleDragEnd}
    >
      <SortableContext 
        items={items}
        strategy={verticalListSortingStrategy}
      >
        {items.map(customer => <SortableCustomerRecord key={customer.id} customer={customer} />)}
      </SortableContext>
    </DndContext>
   </div>
)

原因分析及解决方案

1. 用customer.id时位置重置的原因

你在handleDragEnd里用items.indexOf(active.id)查找索引,但items数组存的是完整的customer对象,而active.id是字符串/数字类型的id值。indexOf无法在对象数组中匹配到基础类型值,导致oldIndex和newIndex都是-1,arrayMove不会修改原数组,最终释放后列表位置重置。

2. 用customer对象时拖拽元素不可见的原因

DnD-Kit要求拖拽id必须是稳定且可序列化的基础类型(字符串、数字),直接传对象作为id会触发两个问题:

  • 内部状态对比依赖对象引用,组件重渲染后transform等拖拽状态无法正确关联到当前元素,导致样式失效,看起来像是“不可见”;
  • 虽然indexOf通过对象引用相等能找到索引,但这不符合DnD-Kit的设计规范,后续容易出现其他异常。

正确解决方式

  • 保持useSortable的id为customer.id(唯一稳定的基础类型值);
  • 修改handleDragEnd的索引查找逻辑,用findIndex根据id匹配对象:
function handleDragEnd(event) {
  const {active, over} = event;
  
  if (active.id !== over.id) {
    setItems((items) => {
      const oldIndex = items.findIndex(item => item.id === active.id);
      const newIndex = items.findIndex(item => item.id === over.id);
      return arrayMove(items, oldIndex, newIndex);
    });
  }
}
  • 可选优化:将SortableContext的items改为id数组,更符合DnD-Kit的设计:
<SortableContext 
  items={items.map(item => item.id)}
  strategy={verticalListSortingStrategy}
>
  {items.map(customer => <SortableCustomerRecord key={customer.id} customer={customer} />)}
</SortableContext>

修改后即可实现拖拽动画正常、释放后位置正确更新的效果。


内容的提问来源于stack exchange,提问作者Gavin Coates

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 02:19:54