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

React多拖拽项问题:多选拖拽时触发React子元素无效错误

问题根源与解决方案

一、错误直接原因

你触发的Objects are not valid as a React child错误,核心是直接把对象当成React子元素渲染了。你的列表项是包含name、checked等字段的对象,而React无法直接渲染整个对象,必须提取对象的具体属性(比如item.name)来渲染。

检查你渲染左右列表的代码,是否存在类似{item}的写法?如果有,改成{item.name}或你需要展示的属性即可解决这个报错。

二、多选拖拽逻辑修正(针对对象数组)

你之前参考的字符串数组示例逻辑无法直接复用,因为对象数组需要处理选中项的收集、拖拽数据的传递和列表更新的逻辑,具体调整如下:

1. 状态管理调整

维护三个核心状态:左右列表的对象数组、选中项的唯一标识集合(建议用id,避免对象引用问题):

import { useState } from 'react';

const App = () => {
  // 初始化左右列表,每个项添加唯一id
  const [leftItems, setLeftItems] = useState([
    { id: 1, name: '选项1', checked: false },
    { id: 2, name: '选项2', checked: false },
    { id: 3, name: '选项3', checked: false }
  ]);
  const [rightItems, setRightItems] = useState([]);
  // 存储选中项的id,而非整个对象
  const [selectedIds, setSelectedIds] = useState([]);

  // ...后续逻辑
};

2. 复选框选中逻辑

让复选框状态与selectedIds联动,点击时添加/移除对应项的id:

const handleCheckboxToggle = (itemId) => {
  setSelectedIds(prev => {
    if (prev.includes(itemId)) {
      return prev.filter(id => id !== itemId);
    } else {
      return [...prev, itemId];
    }
  });
};

3. 拖拽事件处理

  • 拖拽开始:收集所有选中的对象,转成JSON字符串存入dataTransfer(因为dataTransfer只能存储字符串)
  • 拖拽放置:解析JSON数据,更新目标列表和原列表,同时清空选中状态
// 左侧列表的拖拽开始事件
const handleLeftDragStart = (e) => {
  const selectedItems = leftItems.filter(item => selectedIds.includes(item.id));
  // 转成JSON字符串传递
  e.dataTransfer.setData('text/plain', JSON.stringify(selectedItems));
};

// 右侧列表的放置事件
const handleRightDrop = (e) => {
  e.preventDefault();
  const draggedItems = JSON.parse(e.dataTransfer.getData('text/plain'));
  
  // 更新右侧列表:添加拖拽的项
  setRightItems(prev => [...prev, ...draggedItems]);
  // 更新左侧列表:移除选中的项
  setLeftItems(prev => prev.filter(item => !selectedIds.includes(item.id)));
  // 清空选中状态
  setSelectedIds([]);
};

// 右侧列表的拖拽覆盖事件(必须阻止默认行为才能触发drop)
const handleDragOver = (e) => {
  e.preventDefault();
};

4. 正确渲染列表

渲染时必须读取对象的具体属性,同时绑定复选框和拖拽事件:

// 左侧列表
<ul 
  onDragOver={handleDragOver}
  style={{ width: '200px', padding: '10px', border: '1px solid #ccc' }}
>
  {leftItems.map(item => (
    <li 
      key={item.id} 
      draggable 
      onDragStart={handleLeftDragStart}
      style={{ margin: '5px 0' }}
    >
      <input
        type="checkbox"
        checked={selectedIds.includes(item.id)}
        onChange={() => handleCheckboxToggle(item.id)}
      />
      {/* 渲染对象的name属性,而非整个对象 */}
      {item.name}
    </li>
  ))}
</ul>

// 右侧列表
<ul 
  onDrop={handleRightDrop}
  onDragOver={handleDragOver}
  style={{ width: '200px', padding: '10px', border: '1px solid #ccc' }}
>
  {rightItems.map(item => (
    <li key={item.id} style={{ margin: '5px 0' }}>
      {item.name}
    </li>
  ))}
</ul>

三、关键注意点

  • 不要直接渲染对象,必须提取属性值
  • 拖拽传递对象时要转成JSON字符串,接收时再解析
  • 选中项建议用唯一id维护,避免对象引用导致的状态异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:42:34