如何避免鼠标移动时触发context的rerender,仅渲染一次静态页面
方案1:使用useRef存储鼠标坐标,直接操作DOM
你不需要把鼠标位置存在state或者context里,用ref存坐标值,鼠标移动事件触发时只更新ref,然后直接修改对应元素的style属性,完全不触发React的渲染流程。
示例代码片段:
import { useRef, useEffect } from 'react'; function App() { // 用ref存鼠标坐标,更新不会触发重渲染 const mousePos = useRef({ x: 0, y: 0 }); // 用ref获取要移动的元素,不需要走状态绑定style const moveEl = useRef(null); useEffect(() => { const handleMouseMove = (e) => { // 只更新ref,不触发渲染 mousePos.current.x = e.clientX; mousePos.current.y = e.clientY; // 直接修改DOM样式,跳过React渲染 if (moveEl.current) { moveEl.current.style.transform = `translate(${mousePos.current.x * 0.1}px, ${mousePos.current.y * 0.1}px)`; } }; window.addEventListener('mousemove', handleMouseMove); return () => window.removeEventListener('mousemove', handleMouseMove); }, []); return <div ref={moveEl}>需要位移的元素</div> }
这个方案全程只有首次渲染一次,后续鼠标移动完全不触发任何组件重渲染,性能最好。
方案2:如果必须用context共享鼠标状态,拆分context避免重渲染
如果你的场景需要跨多组件共享鼠标位置,不要把坐标值存在context的state里,而是把存储坐标的ref放到context中,context本身的值(ref对象)是永远不变的,所以不会触发消费组件的重渲染,组件内部自己取ref.current的值用即可。
示例代码片段:
import { createContext, useContext, useRef, useEffect } from 'react'; // 创建context,默认值是ref对象 const MouseContext = createContext({ current: { x: 0, y: 0 } }); function Provider({ children }) { const mouseRef = useRef({ x: 0, y: 0 }); useEffect(() => { const handleMouseMove = (e) => { mouseRef.current.x = e.clientX; mouseRef.current.y = e.clientY; }; window.addEventListener('mousemove', handleMouseMove); return () => window.removeEventListener('mousemove', handleMouseMove); }, []); // provider的value永远是同一个ref对象,不会触发重渲染 return <MouseContext.Provider value={mouseRef}>{children}</MouseContext.Provider> } // 子组件消费 function Child() { const mouseRef = useContext(MouseContext); const elRef = useRef(null); // 子组件自己处理位移逻辑,不需要响应状态更新 useEffect(() => { const tick = () => { if (elRef.current) { elRef.current.style.transform = `translate(${mouseRef.current.x * 0.05}px, ${mouseRef.current.y * 0.05}px)`; } requestAnimationFrame(tick); }; const rafId = requestAnimationFrame(tick); return () => cancelAnimationFrame(rafId); }, [mouseRef]); return <div ref={elRef}>子组件位移元素</div> }
这个方案下context不会因为鼠标移动触发任何消费组件重渲染,所有位移逻辑走原生DOM操作和requestAnimationFrame,性能和方案1一致。
方案3:用CSS变量做中间层
还可以把鼠标坐标存在根元素的CSS变量上,所有需要位移的元素直接在CSS里用var()获取值计算位移,全程不需要React参与状态更新:
useEffect(() => { const handleMouseMove = (e) => { document.documentElement.style.setProperty('--mouse-x', `${e.clientX}px`); document.documentElement.style.setProperty('--mouse-y', `${e.clientY}px`); }; window.addEventListener('mousemove', handleMouseMove); return () => window.removeEventListener('mousemove', handleMouseMove); }, []);
对应CSS:
.move-element { transform: translate(calc(var(--mouse-x) * 0.1), calc(var(--mouse-y) * 0.1)); }
这个方案连ref都不用写,纯靠CSS变量联动,代码最简洁。
内容的提问来源于stack exchange,提问作者user1888427
相关产品推荐
相关产品推荐

