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

React方块选择功能bug:切换选中方块时顶部文本短暂消失

问题修复方案

问题根源

  1. 单个Ref复用错误:所有方块共用同一个squareRef,最终只有最后一个方块会被该Ref指向。点击其他方块时,squareRef.current.contains(e.target)返回false,导致mousedown事件先触发取消选中,随后onClick才设置新选中值,造成文本短暂消失。
  2. 事件执行顺序:mousedown事件优先级高于onClick,切换选中时会先执行取消逻辑,再执行选中逻辑,引发视觉闪烁。

修复步骤

  1. 用容器Ref替代单个方块Ref:把所有方块放在一个容器内,给容器添加Ref,统一判断点击是否在方块区域内。
  2. 修正useEffect依赖:useRef的引用本身不会变化,所以useEffect的依赖数组设为空,避免不必要的事件重绑定。
  3. 保留原有功能逻辑:确保点击外部取消选中、点击方块切换选中的功能正常运行。

修复后的完整代码

const { useState, useEffect, useRef } = React;

const App = () => {
  const [selectedSquare, setSelectedSquare] = useState(null);
  // 给方块容器添加Ref
  const squaresContainerRef = useRef(null);

  const handleSquareClick = (squareIndex) => {
    setSelectedSquare(squareIndex);
  };

  useEffect(() => {
    const handler = (e) => {
      // 判断点击是否在方块容器外
      if (squaresContainerRef.current && !squaresContainerRef.current.contains(e.target)) {
        setSelectedSquare(null);
      }
    };
    document.addEventListener("mousedown", handler);

    return () => {
      document.removeEventListener("mousedown", handler);
    };
  }, []); // 空依赖,容器Ref的引用不会变化

  return (
    <div>
      <style>
        {`
        .square-row {
          display: flex;
        }
        
        .square {
          width: 100px;
          height: 100px;
          background-color: #ccc;
          margin-right: 10px;
          margin-top: 10px;
          cursor: pointer;
        }
        
        .selected {
          background-color: orange;
        }
        `}
      </style>
      {selectedSquare && <div>Selected Square: {selectedSquare}</div>}
      {/* 所有方块放在一个容器内,绑定Ref */}
      <div ref={squaresContainerRef}>
        <div className="square-row">
          <div
            className={`square ${selectedSquare === 1 ? 'selected' : ''}`}
            onClick={() => handleSquareClick(1)}
          ></div>
          <div
            className={`square ${selectedSquare === 2 ? 'selected' : ''}`}
            onClick={() => handleSquareClick(2)}
          ></div>
          <div
            className={`square ${selectedSquare === 3 ? 'selected' : ''}`}
            onClick={() => handleSquareClick(3)}
          ></div>
        </div>
        <div className="square-row">
          <div
            className={`square ${selectedSquare === 4 ? 'selected' : ''}`}
            onClick={() => handleSquareClick(4)}
          ></div>
          <div
            className={`square ${selectedSquare === 5 ? 'selected' : ''}`}
            onClick={() => handleSquareClick(5)}
          ></div>
          <div
            className={`square ${selectedSquare === 6 ? 'selected' : ''}`}
            onClick={() => handleSquareClick(6)}
          ></div>
        </div>
      </div>
    </div>
  );
};

ReactDOM.createRoot(document.body).render(<App />);

额外说明

  • 容器Ref的方式更合理,避免了单个Ref复用的问题,能准确判断点击区域。
  • 空依赖的useEffect只会在组件挂载时绑定一次事件,卸载时移除,性能更优。
  • 切换选中时,onClick直接更新选中值,不会被mousedown的取消逻辑干扰,彻底解决文本闪烁问题。

内容的提问来源于stack exchange,提问作者rax-hacks

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 13:17:31