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

React中使用原生DOM操作遇appendChild错误,求代码修改方案

改造原生DOM代码适配React运行环境

原代码核心问题

  1. DOM节点获取时机错误:React组件的JSX还未渲染到真实DOM时,就执行了document.getElementById('container'),此时无法获取到目标节点
  2. 语法错误:circlesArr.forEach((15, i)是无效语法,应为circlesArr.forEach((cols, i)
  3. 未处理内存泄漏:原生事件监听和定时器未在组件卸载时清理,可能引发内存问题

修改后的可运行代码

import { useRef, useEffect } from 'react';

export default function CircleGrid() {
  const containerRef = useRef(null);
  const circlesArr = useRef([]);
  const timerRefs = useRef([]);

  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    // 初始化15x15的circle元素
    for (let i = 0; i < 15; i++) {
      circlesArr.current[i] = [];
      for (let j = 0; j < 15; j++) {
        const circle = document.createElement('div');
        circle.classList.add('circle');
        container.appendChild(circle);
        circlesArr.current[i].push(circle);
      }
    }

    // 定义grow逻辑
    function growCircles(i, j) {
      const circle = circlesArr.current[i]?.[j];
      if (!circle || circle.classList.contains('grow')) return;

      circle.classList.add('grow');
      // 存储定时器以便后续清理
      const timer1 = setTimeout(() => {
        growCircles(i - 1, j);
        growCircles(i + 1, j);
      }, 100);
      const timer2 = setTimeout(() => {
        circle.classList.remove('grow');
      }, 300);
      timerRefs.current.push(timer1, timer2);
    }

    // 绑定点击事件
    circlesArr.current.forEach((cols, i) => {
      cols.forEach((circle, j) => {
        const handleClick = () => growCircles(i, j);
        circle.addEventListener('click', handleClick);
        // 存储事件处理函数以便卸载时移除
        circle._clickHandler = handleClick;
      });
    });

    // 组件卸载时清理资源
    return () => {
      // 清除所有定时器
      timerRefs.current.forEach(timer => clearTimeout(timer));
      // 移除所有事件监听并清理DOM
      circlesArr.current.forEach(cols => {
        cols.forEach(circle => {
          circle.removeEventListener('click', circle._clickHandler);
          circle.remove();
        });
      });
    };
  }, []);

  return <div ref={containerRef} className="container"></div>;
}

更符合React范式的实现方案

上面的方案仍依赖原生DOM操作,更推荐用React的JSX和状态管理实现,避免直接操作DOM:

import { useState } from 'react';

export default function CircleGrid() {
  // 用二维数组存储每个circle的grow状态
  const [growStates, setGrowStates] = useState(
    Array(15).fill().map(() => Array(15).fill(false))
  );

  const growCircles = (i, j) => {
    if (i < 0 || i >= 15 || j < 0 || j >= 15 || growStates[i][j]) return;

    // 更新当前circle的grow状态
    setGrowStates(prev => {
      const newStates = [...prev];
      newStates[i] = [...newStates[i]];
      newStates[i][j] = true;
      return newStates;
    });

    // 100ms后触发上下circle的grow
    setTimeout(() => {
      growCircles(i - 1, j);
      growCircles(i + 1, j);
    }, 100);

    // 300ms后取消grow状态
    setTimeout(() => {
      setGrowStates(prev => {
        const newStates = [...prev];
        newStates[i] = [...newStates[i]];
        newStates[i][j] = false;
        return newStates;
      });
    }, 300);
  };

  return (
    <div className="container">
      {Array(15).fill().map((_, i) => (
        <div key={i} className="row">
          {Array(15).fill().map((_, j) => (
            <div
              key={`${i}-${j}`}
              className={`circle ${growStates[i][j] ? 'grow' : ''}`}
              onClick={() => growCircles(i, j)}
            />
          ))}
        </div>
      ))}
    </div>
  );
}

说明

  • 第一种方案保留原代码的原生DOM操作逻辑,通过useRef和useEffect确保DOM节点存在时执行操作,并添加资源清理逻辑
  • 第二种方案完全使用React状态管理和JSX渲染,更符合React设计理念,避免直接操作DOM的潜在问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 04:10:35