React 前端导航

React 实现埋点的 hooks,自动上报 pv 和 click 事件

React 实现埋点的 hooks,自动上报 pv 和 click 事件

本文将围绕 React 中的自定义 hooks 来介绍 hooks 的基本概念以及如何来设计、编写我们自己的 hooks 。

React hooks 介绍

自定义 hooks 可以让我们据业务需求制定满足业务需要的组合 hooks,专注于状态逻辑的复用,它是基于 React Hooks 的一个拓展。自定义 hooks 的初衷就是把一段逻辑封装起来,做到复用。

自定义 hooks 也可以说是 React Hooks 的聚合产物,其内部有一个或者多个 React Hooks 组成,用于解决复杂逻辑的复用问题。

传统的自定义 hooks 就像下面这样:

import React, { useState,useEffect } from "react";

function useMyHook(paramA, paramB, ...) {
  /* 
    实现自定义 hooks 逻辑
    内部应用了其他 React Hooks,例如useState,useEffect
  */
  const [loading,setLoading] = useState(false);
  const [data,setData] = useState();
  
  // 请求某些数据
  useEffect(()=>{
    setLoading(true);
    fetch(url).then((res)=>{
        // 这里就是你的数据处理逻辑
        ...
    });
  },[])


  return {
    loading,
    data
  }
}

使用:

const {loading,data} = useMyHook(paramA, paramB, ...)

自定义 hooks 参数 可能是以下内容:

  • hooks 初始化值
  • 一些副作用或事件的回调函数
  • 可以是 useRef 获取的 DOM 元素或者组件实例
  • 不需要参数

自定义 hooks 返回值 可能是以下内容:

  • 负责渲染视图获取的状态
  • 更新函数组件方法,本质上是 useState 或者 useReducer
  • 一些传递给子孙组件的状态
  • 没有返回值

React hooks 特性

首先我们必须要知道,自定义 hooks 本质上就是一个函数,而且是在函数组件中被执行。自定义 hooks 驱动本质上就是函数组件的执行。

驱动条件

自定义 hooks 的驱动条件主要有两点:

  • props 改变带来的函数组件执行。
  • useState 或 useReducer 改变 state 引起函数组件的更新。
顺序原则

自定义 hooks 内部至少要有一个 React Hooks,那么自定义 hooks 也同样要遵循 React Hooks 的规则,不能放在条件语句中,而且要保持执行顺序的一致性。

这是为什么呢?

因为在更新过程中,如果通过 if 条件语句,增加或者删除 hooks,那么在复用 hooks 的过程中,会产生复用 hooks 状态和当前 hooks 不一致的问题。所以在开发时一定要注意 hooks 顺序的一致性。

埋点 hooks 的实现

接着我们来实现一个能够自动上报页面浏览量|点击时间的自定义 hooks -- useLog。

通过这个自定义 hooks,来控制监听 DOM 元素,分清楚依赖关系。

代码实现如下 :

export const LogContext = createContext({});

export const useLog = () => {
  /* 公共参数 */
  const message = useContext(LogContext);
  const listenDOM = useRef(null);

  /* 分清依赖关系 */
  const reportMessage = useCallback(
    function (data, type) {
      if (type === "pv") {
        // 页面浏览量上报
        console.log("组件 pv 上报", message);
      } else if (type === "click") {
        // 点击上报
        console.log("组件 click 上报", message, data);
      }
    },
    [message]
  );

  useEffect(() => {
    const handleClick = function (e) {
      reportMessage(e.target, "click");
    };
    if (listenDOM.current) {
      listenDOM.current.addEventListener("click", handleClick);
    }

    return function () {
      if (listenDOM.current) {
        listenDOM.current.removeEventListener("click", handleClick);
      };
  }, [reportMessage]);

  return [listenDOM, reportMessage];
};

在上面的代码中,使用到了如下4个 React Hooks:

  • 使用 useContext 获取埋点的公共信息,当公共信息改变时,会统一更新。
  • 使用 useRef 获取 DOM 元素。
  • 使用 useCallback 缓存上报信息 reportMessage 方法,里面获取 useContext 内容。把 context 作为依赖项,当依赖项发生改变时,重新声明 reportMessage 函数。
  • 使用 useEffect 监听 DOM 事件,把 reportMessage 作为依赖项,在 useEffect 中进行事件绑定,返回的销毁函数用于解除绑定。

依赖关系:context 发生改变 -> 让引入 context 的 reportMessage 重新声明 -> 让绑定 DOM 事件监听的 useEffect 里面能够绑定最新的 reportMessage

使用 useLog:

import React, { useState } from "react";
import { LogContext, useLog } from "./hooks/useLog";

const Home = () => {
  const [dom, reportMessage] = useLog();
  return (
    <div>
      {/* 监听内部点击 */}
      <div ref={dom}>
        <button> 按钮 1 (内部点击) </button>
        <button> 按钮 2 (内部点击) </button>
        <button> 按钮 3 (内部点击) </button>
      </div>
      {/* 外部点击 */}
      <button
        onClick={() => {
          console.log(reportMessage);
        }}
      >
        外部点击
      </button>
    </div>
  );
};
// 阻断 useState 的更新效应
const Index = React.memo(Home);

const App = () => {
  const [value, setValue] = useState({});
  return (
    <LogContext.Provider value={value}>
      <Index />
      <button onClick={() => setValue({ name: "张三", age: 18 })}>
        点击
      </button>
    </LogContext.Provider>
  );
};

export default App;

当 context 发生改变时,能够达到正常上报的效果。小细节:使用 React.memo 来阻断 App 组件改变 state 给 Home 组件带来的更新效应。

声明:本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。邮箱:farmerlzj@163.com。 本站原创内容未经允许不得转载,或转载时需注明出处: 内容转载自: React前端网:https://qianduan.shop/blogs/detail/78

#react#hooks

相关推荐

react中实现markdown文件读取展示

react中实现markdown文件读取展示

umi实践问题汇总--持续更新

在使用umi的过程中所遇到问题的记录汇总