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

React中使用cloneElement处理按钮子元素时出现类型错误问题

问题描述

尝试通过cloneElement添加按钮的子元素(div)时,触发如下错误:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

使用导入的组件(如<Test />)调用cloneElement时正常,但用event.currentTarget.children[0]就报错。相关代码如下:

const [CurrentIndex, setCurrentIndex] = useState(0);
const [Symbols, setSymbols] = useState([{ symbols: [] }]);

const appendToCurrent = (event) => {
 const clonedElement = cloneElement(
  event.currentTarget.children[0]
 );
 setSymbols(prev =>
  Object.assign([], prev, {
  [CurrentIndex]: {
   symbols: [...prev[CurrentIndex].symbols, clonedElement]
  }
 }))
}

<button onClick={event => appendToCurrent(event)}><div>A</div></button>                   
<button onClick={event => appendToCurrent(event)}><div>B</div></button>                    

{Symbols.map((field, i) => {
    return (
      <div key={i}>
        <div onClick={() => setCurrentIndex(i)}>
          {field.symbols.map((symbol, i) => {
            return (
              <div key={i}>
                {symbol}
              </div>
            );
          })}
        </div>
      </div>
    );
})}
原因与解决方案

核心原因

event.currentTarget.children[0]拿到的是浏览器原生DOM节点,但React的cloneElement方法要求第一个参数必须是React虚拟元素(JSX编译后的对象),两者类型完全不匹配,直接传入就会报错。而你用<Test />这类组件能成功,是因为它们本身就是React虚拟元素,符合参数要求。

解决方法

不要直接从DOM里拿元素,而是提前把要添加的内容作为React元素存储,通过点击事件传递标识来获取对应的元素进行克隆。修改后的代码如下:

import { useState, cloneElement } from 'react';

const [CurrentIndex, setCurrentIndex] = useState(0);
const [Symbols, setSymbols] = useState([{ symbols: [] }]);

// 提前定义好需要的React元素
const availableSymbols = {
  A: <div>A</div>,
  B: <div>B</div>
};

const appendToCurrent = (symbolKey) => {
  // 直接使用预定义的React元素克隆
  const clonedElement = cloneElement(availableSymbols[symbolKey]);
  setSymbols(prev => {
    // 正确更新数组状态,避免Object.assign的潜在问题
    const updatedSymbols = [...prev];
    updatedSymbols[CurrentIndex] = {
      ...updatedSymbols[CurrentIndex],
      symbols: [...updatedSymbols[CurrentIndex].symbols, clonedElement]
    };
    return updatedSymbols;
  });
};

// 点击时传递对应的标识,不再操作DOM
<button onClick={() => appendToCurrent('A')}><div>A</div></button>                   
<button onClick={() => appendToCurrent('B')}><div>B</div></button>                    

{Symbols.map((field, i) => {
    return (
      <div key={i}>
        <div onClick={() => setCurrentIndex(i)}>
          {field.symbols.map((symbol, idx) => (
            <div key={idx}>{symbol}</div>
          ))}
        </div>
      </div>
    );
})}

额外提示

React的设计思路是通过状态管理虚拟DOM,尽量避免直接操作原生DOM。如果需要动态生成内容,优先通过状态存储数据或React元素,再根据状态渲染视图,这样能保证状态和视图的一致性,也符合React的最佳实践。

内容的提问来源于stack exchange,提问作者s.kuznetsov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 14:37:52