创见博客
useRef的应用之首次渲染埋点上报
七崽爱吃小饼干2026/01/08阅读 0专栏 React

一、为什么 useRef 适合做首次渲染判断?

核心原因是:

  1. useRef 创建的对象在组件整个生命周期内唯一且持久化,不会因重渲染重置;
  2. 修改 ref.current 不会触发组件重渲染,不会带来额外的性能开销;
  3. 可以通过一个布尔值(比如 isFirstRender)标记首次渲染状态,一旦标记为 false,后续重渲染不会改变。

二、实现首次渲染埋点的完整示例

下面是一个可直接复用的代码示例,核心逻辑是用 useRef 标记首次渲染,结合 useEffect 完成埋点上报(无依赖的 useEffect 仅在组件挂载时执行,但配合 ref 可精准区分“首次挂载”和“后续重渲染”):

jsx
import { useRef, useEffect } from 'react';

// 模拟埋点上报函数(实际项目中替换为真实的埋点接口)
const reportBuriedPoint = (eventName) => {
  console.log(`埋点上报:${eventName},时间:${new Date().toLocaleString()}`);
  // 真实场景:调用后端接口或第三方埋点 SDK
  // fetch('/api/buried-point', {
  //   method: 'POST',
  //   body: JSON.stringify({ eventName, page: '首页', time: new Date() })
  // });
};

function MyComponent() {
  // 1. 创建 ref 标记是否为首次渲染,初始值为 true
  const isFirstRenderRef = useRef(true);

  // 2. 核心逻辑:仅在首次渲染时执行埋点
  useEffect(() => {
    // 首次渲染时执行埋点
    if (isFirstRenderRef.current) {
      // 上报“组件首次渲染”埋点
      reportBuriedPoint('my_component_first_render');
      // 标记为非首次渲染,后续重渲染不再执行
      isFirstRenderRef.current = false;
    }

    // 可选:组件卸载时的清理逻辑(比如取消埋点请求)
    return () => {
      console.log('组件卸载,可执行埋点清理');
    };
  }, []); // 空依赖:仅在组件挂载时执行一次 useEffect

  // 模拟组件重渲染(点击按钮触发 state 更新)
  const handleClick = () => {
    // 这里即使触发重渲染,isFirstRenderRef.current 已为 false,埋点不会重复上报
    console.log('触发组件重渲染');
  };

  return (
    <div>
      <h3>首次渲染埋点示例</h3>
      <button onClick={handleClick}>触发重渲染(不会重复上报)</button>
    </div>
  );
}

export default MyComponent;

三、关键细节解释

  1. 核心逻辑:

    • isFirstRenderRef 初始值为 true,表示“未完成首次渲染”;
    • 无依赖的 useEffect 仅在组件挂载(首次渲染) 时执行一次;
    • 执行埋点后,立即将 isFirstRenderRef.current 设为 false,后续即使组件重渲染(比如点击按钮),这个值也不会重置,因此埋点只会执行一次。
  2. 为什么不用普通变量? 如果用普通变量 let isFirstRender = true,组件每次重渲染时,这个变量会被重新声明为 true,导致埋点重复执行;而 useRef 的值是持久化的,不会因重渲染重置。

  3. 为什么不用 useState? useState 修改会触发重渲染,而“首次渲染标记”只是一个内部状态,不需要驱动视图更新,用 useState 会多一次无意义的重渲染,不符合性能最佳实践。

四、进阶:封装成通用 Hook(复用性更高)

如果多个组件需要做首次渲染埋点,可以封装成通用 Hook,简化代码:

jsx
import { useRef, useEffect } from 'react';

// 通用 Hook:判断首次渲染并执行回调(比如埋点)
export function useFirstRender(callback) {
  const isFirstRenderRef = useRef(true);

  useEffect(() => {
    if (isFirstRenderRef.current) {
      // 执行传入的回调(埋点逻辑)
      callback?.();
      isFirstRenderRef.current = false;
    }
  }, [callback]); // 依赖 callback,避免闭包问题
}

// 组件中使用
function MyComponent() {
  // 直接调用通用 Hook,传入埋点逻辑
  useFirstRender(() => {
    reportBuriedPoint('my_component_first_render');
  });

  return <div>使用通用 Hook 的首次渲染埋点</div>;
}

总结

  1. useRef 是判断组件首次渲染的最优方案,既持久化又不触发重渲染,无性能开销;
  2. 核心实现:用 ref.current 存储布尔标记,结合无依赖的 useEffect,首次渲染时执行埋点并修改标记;
  3. 可封装成通用 Hook 提升复用性,避免重复代码,符合 React 最佳实践。

这种方式能确保埋点仅在组件真正首次渲染时上报,不会因组件重渲染、父组件更新等情况重复上报,是生产环境中最常用的埋点实现方式。

评论
0/100