React.js实现3D立方体悬停旋转遇最大更新深度超限错误,求方案
React 3D立方体悬停旋转问题排查与优化
错误原因分析
- 你的
useEffect依赖了deg和mouseover两个状态,当mouseover为true时,setDeg(deg+1)会更新deg,触发组件重渲染 - 重渲染后,
useEffect因为deg变化再次执行,又调用setDeg(deg+1),形成无限循环,最终导致React抛出“最大更新深度超限”的警告 - 另外,
else分支里的setDeg(deg)完全多余,deg本身没有变化,强行调用setState会触发不必要的重渲染
更优实现方案
核心思路:用浏览器原生的requestAnimationFrame实现流畅帧动画,通过控制动画的启停来实现悬停旋转、离开停止的效果,彻底避免state循环更新的问题。
优化后的代码示例:
import { useState, useEffect, useRef } from 'react'; function Nav() { const [deg, setDeg] = useState(0); const [isHovering, setIsHovering] = useState(false); // 用ref存储动画帧ID,方便后续取消动画 const animationRef = useRef(null); // 每帧更新立方体角度的逻辑 const rotateCube = () => { // 用函数式更新确保拿到最新的deg值 setDeg(prevDeg => prevDeg + 1); animationRef.current = requestAnimationFrame(rotateCube); }; useEffect(() => { if (isHovering) { // 鼠标悬停时启动动画 animationRef.current = requestAnimationFrame(rotateCube); } else { // 鼠标离开时停止动画 if (animationRef.current) { cancelAnimationFrame(animationRef.current); } } // 组件卸载时清理动画,避免内存泄漏 return () => { if (animationRef.current) { cancelAnimationFrame(animationRef.current); } }; }, [isHovering]); return ( <div className='container'> <button className='btn btn1' onMouseEnter={() => setIsHovering(true)} onMouseLeave={() => setIsHovering(false)} > button </button> <div className="cube" style={{transform: `rotate3d(0, 1, 0, ${deg}deg)`}}> <div className="side front">HIHI</div> <div className="side left">sadfasfasdf</div> <div className="side right">fffffffffffffffff</div> <div className="side back"> sadfjadois fjaisdof</div> <div className="side top"> asdfj siodajf iaosd</div> <div className="side bottom"> asdfadsi jaosdij f</div> </div> <button className='btn btn1' onMouseEnter={() => setIsHovering(true)} onMouseLeave={() => setIsHovering(false)} > button </button> </div> ); }
方案优势
- 借助
requestAnimationFrame实现浏览器原生流畅动画,性能远优于频繁更新state的方式 - 用
useRef存储动画ID,精准控制动画启停,不会出现内存泄漏 - 仅在
isHovering状态变化时执行逻辑,彻底避免无限循环问题 - 函数式更新
setDeg(prevDeg => prevDeg + 1)确保每次都能拿到最新的角度值
内容的提问来源于stack exchange,提问作者user17833823
相关产品推荐
相关产品推荐

