You need to enable JavaScript to run this app. 如何避免鼠标移动时触发context的rerender,仅渲染一次静态页面
优惠活动
大模型
产品
解决方案
定价
更多

如何避免鼠标移动时触发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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 06:45:01