React方块选择功能bug:切换选中方块时顶部文本短暂消失
问题修复方案
问题根源
- 单个Ref复用错误:所有方块共用同一个
squareRef,最终只有最后一个方块会被该Ref指向。点击其他方块时,squareRef.current.contains(e.target)返回false,导致mousedown事件先触发取消选中,随后onClick才设置新选中值,造成文本短暂消失。 - 事件执行顺序:
mousedown事件优先级高于onClick,切换选中时会先执行取消逻辑,再执行选中逻辑,引发视觉闪烁。
修复步骤
- 用容器Ref替代单个方块Ref:把所有方块放在一个容器内,给容器添加Ref,统一判断点击是否在方块区域内。
- 修正useEffect依赖:
useRef的引用本身不会变化,所以useEffect的依赖数组设为空,避免不必要的事件重绑定。 - 保留原有功能逻辑:确保点击外部取消选中、点击方块切换选中的功能正常运行。
修复后的完整代码
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
相关产品推荐
相关产品推荐

