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

react-beautiful-dnd:拖拽结束后状态变更引发异常行为

解决react-beautiful-dnd表格拖拽排序后的重渲染异常问题

从你提供的动图和代码来看,拖拽结束后的异常行为主要由依赖数组索引的回调、状态更新不符合不可变性、不必要的重渲染这几个问题导致,下面是具体的修复方案:

核心问题分析

  1. 回调函数依赖数组索引:deleteArticle、setShowPopUp和updateFieldChanged都用数组索引定位元素,拖拽后数组顺序改变,旧索引会指向错误的元素。
  2. 状态更新未遵循不可变性:如果onDragEnd中直接修改原数组,React无法正确识别状态变化,引发重渲染异常。
  3. 不必要的重渲染:updateFieldChanged(index)每次渲染都会生成新函数,触发组件无意义重渲染,干扰拖拽后的状态同步。

具体修复步骤

1. 替换索引依赖为唯一ID

所有操作元素的回调都改用元素的_id(唯一标识)来定位,避免拖拽后索引失效:

  • 修改setShowPopUp和deleteArticle的调用:
    // 原代码:onClick={() => setShowPopUp(index)}
    onClick={() => setShowPopUp(element._id)}
    
    // 原代码:onClick={() => deleteArticle(index)}
    onClick={() => deleteArticle(element._id)}
    
  • 父组件中对应函数也要改为根据ID处理:
    // deleteArticle示例
    const deleteArticle = (id) => {
      setArticles(prev => prev.filter(item => item._id !== id));
    };
    
    // setShowPopUp示例
    const setShowPopUp = (id) => {
      const targetItem = Articles.find(item => item._id === id);
      // 处理弹窗逻辑
    };
    

2. 优化updateFieldChanged的回调方式

避免每次渲染生成新函数,改用函数柯里化+唯一ID的方式,同时保证状态更新的不可变性:

  • 父组件中重构updateFieldChanged:
    import { useCallback } from 'react';
    
    const updateFieldChanged = useCallback((id) => (e) => {
      const { name, value } = e.target;
      setArticles(prev => prev.map(item => 
        item._id === id ? {...item, [name]: value} : item
      ));
    }, []);
    
  • 表格组件中调用时传递元素ID:
    // 原代码:onChange={updateFieldChanged(index)}
    onChange={updateFieldChanged(element._id)}
    

3. 确保onDragEnd的状态更新符合不可变性

拖拽排序时必须创建新数组,不能直接修改原数组:

import { useCallback } from 'react';

const onDragEnd = useCallback((result) => {
  if (!result.destination) return;
  // 复制原数组,避免直接修改
  const items = Array.from(Articles);
  const [reorderedItem] = items.splice(result.source.index, 1);
  items.splice(result.destination.index, 0, reorderedItem);
  setArticles(items);
}, [Articles]);

4. 优化组件重渲染性能

用React.memo包裹表格组件,配合父组件中useCallback稳定回调函数,减少不必要的重渲染:

// 表格组件末尾
export default React.memo(EditCategoryTable);

修改后的完整组件代码

import React from "react";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";

const EditCategoryTable = ({
  onDragEnd,
  Articles,
  updateFieldChanged,
  setShowPopUp,
  deleteArticle,
}) => {
  return (
    <table className="articles_table">
      <thead>
        <tr>
          <th style={{ width: "5%" }}></th>
          <th style={{ width: "55%" }}>Article</th>
          <th style={{ width: "20%" }}>Price</th>
          <th style={{ width: "10%" }}></th>
          <th style={{ width: "10%" }}></th>
        </tr>
      </thead>
      <DragDropContext onDragEnd={onDragEnd}>
        <Droppable droppableId="droppable-1">
          {(provided, _) => (
            <tbody ref={provided.innerRef} {...provided.droppableProps}>
              {Articles.map((element, index) => (
                <Draggable
                  key={element._id}
                  draggableId={`draggable-${element._id}`}
                  index={index}
                >
                  {(provided, snapshot) => (
                    <tr
                      ref={provided.innerRef}
                      {...provided.draggableProps}
                      style={{
                        ...provided.draggableProps.style,
                        boxShadow: snapshot.isDragging
                          ? "0 0 .4rem #666"
                          : "none",
                      }}
                    >
                      <td className="Movebuttons">
                        <i
                          className="fa-solid fa-bars"
                          {...provided.dragHandleProps}
                        ></i>
                      </td>

                      <td>
                        <input
                          className="articles_table_input"
                          type="text"
                          id="name"
                          name="name"
                          value={element.name || ""}
                          onChange={updateFieldChanged(element._id)}
                        />
                      </td>
                      <td>
                        <input
                          className="noscroll articles_table_input"
                          type="number"
                          id="price"
                          name="price"
                          value={element.price || ""}
                          onChange={updateFieldChanged(element._id)}
                        />
                      </td>
                      <td>
                        <i
                          className="fa-regular fa-pen-to-square fa-lg"
                          style={{ color: "#018a19" }}
                          onClick={() => setShowPopUp(element._id)}
                        ></i>
                      </td>
                      <td>
                        <i
                          className="fa-solid fa-trash fa-lg"
                          onClick={() => deleteArticle(element._id)}
                          style={{ color: "#454545" }}
                        ></i>
                      </td>
                    </tr>
                  )}
                </Draggable>
              ))}
              {provided.placeholder}
            </tbody>
          )}
        </Droppable>
      </DragDropContext>
    </table>
  );
};

export default React.memo(EditCategoryTable);

内容的提问来源于stack exchange,提问作者Zidane Hammouda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 14:35:33