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

如何修复React中Cannot read properties of undefined (reading 'classList')错误

React ChildrenList组件鼠标进入事件错误修复方案

问题场景

在React开发的ChildrenList组件中,触发鼠标进入事件时抛出错误:

TypeError: Cannot read properties of undefined (reading 'classList')
错误定位在handleOnMouseEnterDetailButton函数,组件代码如下:

import { useCollectionData } from "react-firebase-hooks/firestore";
import { collection, deleteDoc, doc } from "firebase/firestore";
import db from "../../firebase";
import { useGlobalContext } from "../../context";
import styles from "./childreList.module.css";
import { PiDotsThreeOutlineVerticalDuotone } from "react-icons/pi";
import { GoTrash } from "react-icons/go";
const ChildrenList = ({ path }) => {
  const { value, categoryName } = useGlobalContext();
  const query = collection(db, path);
  const [docs, loading, error] = useCollectionData(query);
  
  const handleDeleteSubCollection = async (uuid) => {
    await deleteDoc(
      doc(db, `users/${value.uid}/children/${categoryName}/children2/${uuid}`)
    );
  };
  const handleOnMouseLeaveDetailButton = (e) => {
    e.target.children[1].classList.remove(styles.detail_button_active);
  };
  const handleOnMouseEnterDetailButton = (e) => {
    e.target.children[1].classList.add(styles.detail_button_active);
    console.log(e.target.children);
  };
  const handleOnClickAdd = (e) => {
    console.log(e.target.previousSibling);
    e.target.nextSibling.classList.add(styles.delete_button_active);
  };
  const handleOnMouseLeaveRemove = (e) => {
    e.target.nextSibling.classList.remove(styles.delete_button_active);
  };
  return (
    <div className={styles.container}>
      {docs?.map((item, index) => {
        return (
          <div
            onMouseEnter={handleOnMouseEnterDetailButton}
            onMouseLeave={handleOnMouseLeaveDetailButton}
            className={styles.container_single_card}
            key={index}
          >
            <div className={styles.word_part}>
              {item.englishWord}
              {/* {item.turkishWord} */}
            </div>
            {/* details */}
            <button
              className={styles.detail_button}
              onClick={handleOnClickAdd}
              onMouseLeave={handleOnMouseLeaveRemove}
            >
              <PiDotsThreeOutlineVerticalDuotone />
            </button>
            {/* delete */}
            <button
              className={styles.delete_button}
              onClick={() => handleDeleteSubCollection(item.uuid)}
            >
              <GoTrash />
            </button>
          </div>
        );
      })}
    </div>
  );
};
export default ChildrenList;

错误原因

  1. e.target指向不固定:鼠标进入外层容器时,e.target可能指向内部的文本区块、按钮甚至图标元素,而非绑定事件的外层容器。此时e.target.children[1]会因目标元素没有足够子元素返回undefined,访问classList时就会抛出错误。
  2. DOM结构依赖过强:直接通过索引获取子元素的写法,完全依赖固定的DOM层级,一旦页面结构调整(比如新增元素),代码直接失效。

修复方案

方案一:使用React状态管理(推荐)

遵循React数据驱动的理念,用状态控制类名,放弃直接操作DOM:

import { useCollectionData } from "react-firebase-hooks/firestore";
import { collection, deleteDoc, doc } from "firebase/firestore";
import db from "../../firebase";
import { useGlobalContext } from "../../context";
import styles from "./childreList.module.css";
import { PiDotsThreeOutlineVerticalDuotone } from "react-icons/pi";
import { GoTrash } from "react-icons/go";
import { useState } from "react";

const ChildrenList = ({ path }) => {
  const { value, categoryName } = useGlobalContext();
  const query = collection(db, path);
  const [docs, loading, error] = useCollectionData(query);
  // 记录当前hover的卡片索引
  const [hoverIndex, setHoverIndex] = useState(-1);
  // 记录当前激活删除按钮的索引
  const [activeDeleteIndex, setActiveDeleteIndex] = useState(-1);

  const handleDeleteSubCollection = async (uuid) => {
    await deleteDoc(
      doc(db, `users/${value.uid}/children/${categoryName}/children2/${uuid}`)
    );
  };

  return (
    <div className={styles.container}>
      {docs?.map((item, index) => {
        const isHovered = hoverIndex === index;
        const isDeleteActive = activeDeleteIndex === index;
        return (
          <div
            onMouseEnter={() => setHoverIndex(index)}
            onMouseLeave={() => setHoverIndex(-1)}
            className={styles.container_single_card}
            key={index}
          >
            <div className={styles.word_part}>
              {item.englishWord}
              {/* {item.turkishWord} */}
            </div>
            {/* details */}
            <button
              className={`${styles.detail_button} ${isHovered ? styles.detail_button_active : ''}`}
              onClick={() => setActiveDeleteIndex(index)}
              onMouseLeave={() => setActiveDeleteIndex(-1)}
            >
              <PiDotsThreeOutlineVerticalDuotone />
            </button>
            {/* delete */}
            <button
              className={`${styles.delete_button} ${isDeleteActive ? styles.delete_button_active : ''}`}
              onClick={() => handleDeleteSubCollection(item.uuid)}
            >
              <GoTrash />
            </button>
          </div>
        );
      })}
    </div>
  );
};
export default ChildrenList;

方案二:修正事件目标,确保操作正确DOM元素

如果坚持直接操作DOM,需要确保始终获取到绑定事件的外层容器,同时处理按钮内部图标的目标指向问题:

// 修改鼠标进入/离开容器的处理函数
const handleOnMouseLeaveDetailButton = (e) => {
  // currentTarget始终指向绑定事件的外层容器
  e.currentTarget.children[1].classList.remove(styles.detail_button_active);
};
const handleOnMouseEnterDetailButton = (e) => {
  e.currentTarget.children[1].classList.add(styles.detail_button_active);
};

// 修改按钮相关处理函数,避免target指向内部图标
const handleOnClickAdd = (e) => {
  const button = e.target.closest('button');
  if (button) {
    button.nextSibling.classList.add(styles.delete_button_active);
  }
};
const handleOnMouseLeaveRemove = (e) => {
  const button = e.target.closest('button');
  if (button) {
    button.nextSibling.classList.remove(styles.delete_button_active);
  }
};

内容的提问来源于stack exchange,提问作者Utku Ergün

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:43:28