React 实现自动上报 PV/Click 的埋点 Hooks

news2024/11/14 19:51:21

自定义 hooks 是基于 React Hooks 的一个拓展,我们可以根据业务需求制定满足业务需要的组合 hooks,更注重的是逻辑单元。怎样把一段逻辑封装起来,做到复用,这才是自定义 hooks 的初衷。

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

一个传统自定义 hooks 长下面这个样子:

function useXXX(参数A, 参数B, ...) {
  /* 
    实现自定义 hooks 逻辑
    内部应用了其他 React Hooks
  */
  return [xxx, ...]
}

使用:

const [xxx, ...] = useXXX(参数A, 参数B, ...)

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

  • hooks 初始化值

  • 一些副作用或事件的回调函数

  • 可以是 useRef 获取的 DOM 元素或者组件实例

  • 不需要参数

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

  • 负责渲染视图获取的状态

  • 更新函数组件方法,本质上是 useState 或者 useReducer

  • 一些传递给子孙组件的状态

  • 没有返回值

 特性

首先我们要明白,开发者编写的自定义 hooks 本质上就是一个函数,而且在函数组件中被执行。自定义 hooks 驱动本质上就是函数组件的执行

驱动条件

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

  1. props 改变带来的函数组件执行。

  2. useState 或 useReducer 改变 state 引起函数组件的更新。

 

 顺序原则

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

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

 实践

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

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

编写自定义 hooks:

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 () {
      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 

使用自定义 hooks:

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({ cat: "小猫", color: "棕色" })}>
        点击
      </button>
    </LogContext.Provider>
  );
};

export default App;

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

结果

刚开始时依次点击按钮1,2,3,效果如下:

点击点击按钮后,再依次点击按钮1,2,3时,效果如下:

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【并发编程十三】c++原子操作

【并发编程十三】c原子操作一、改动序列1、改动序列2、预测执行二、原子操作及其类别1、原子操作2、非原子操作3、原子类型三、标准原子类型1、标准原子类型的两种实现方式2、原子操作的用途3、原子操作的宏四、操作std:atomic_flag1、简介2、使用说明3、使用std:atomic_flag实…

Kettle(16):Kitchen作业执行引擎

在Linux中对Kettle做Linux配置(和Windows相同,添加驱动jar包) 上传以后需要重启。 1 在Windows中开发作业 2 配置Start组件 3 配置转换组件 修改Kettle&

【HBase——陌陌海量存储案例】1.案例介绍与HBase表结构设计(上)

前言 本系列接【HBase入门】系列文章后实战案例的学习。 学习目标 能够掌握HBase表结构设计&#xff08;表设计、ROWKEY设计、预分区&#xff09; 能够安装部署Apache Phoenix 能够掌握Phoenix的基本操作 能够掌握使用Phoenix建立二级索引提升性能 能够基于Phoenix JDBC API编…

Ubuntu安装ROS(每个步骤图文详细)

Ubuntu安装ROS&#xff08;每个步骤图文详细&#xff09;前言&#xff08;推荐安装&#xff09;ROS对应的Ubuntu的版本换源安装ROS一、添加ROS软件源二、添加密钥三、安装ROS-melodic四、初始化rosdep可能出现的问题&#xff1a;一 、 **sudo: rosdep&#xff1a;找不到命令**二…

算法_位运算x(-x)和x(x-1)

最近在跟着y总学算法。 今天学到了两个很经典的位运算&#xff0c;x&(-x)和x&(x-1)&#xff1a; x&(-x)&#xff1a;保留二进制下最后出现的1的位置&#xff0c;其余位置置0&#xff08;即一个数中最大的2的n次幂的因数 x&(x-1)&#xff1a;消除二进制下最后…

SpringCloud整合Zookeeper代替Eureka

目录 一、注册中心Zookeeper 二、服务提供者 三、服务消费者 一、注册中心Zookeeper zookeeper是一个分布式协调工具&#xff0c;可以实现注册中心功能 关闭Linux服务器防火墙后启动zookeeper服务器 zookeeper服务器取代Eureka服务器&#xff0c;zk作为服务注册中心 Lin…

ORA-600 kcbzpbuf_1故障恢复----惜分飞

数据库启动报错ORA-03113SQL> startup;ORACLE instance started. Total System GlobalArea 5.1310E10 bytesFixed Size 2265224 bytesVariable Size 1.8119E10 bytesDatabaseBuffers 3.3152E10 bytesRedo Buffers 36069376 bytesDatabasemounted. ORA-03113: end-of-file on…

二叉树的层序遍历

二叉树的层序遍历 层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。 需要借用一个辅助数据结构即队列来实现&#xff0c;队列先进先出&#xff0c;符合一层一层遍历的逻辑&#xff0c;而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。 而这种层序遍历方式就是…

Java:基于XML的Spring使用

基于XML的Spring使用一、Spring IOC 底层实现1.1 BeanFactory与ApplicationContexet1.2 图解IOC类的结构二、 Spring依赖注入数值问题【重点】2.1 字面量数值2.2 CDATA区2.3 外部已声明bean及级联属性赋值2.4 内部bean2.5 集合三、 Spring依赖注入方式【基于XML】3.1 set注入3.…

白炽灯护眼还是LED护眼?盘点专业护眼的LED护眼灯

目前大多数家庭都会购买台灯使用&#xff0c;选择白炽灯还是LED灯呢&#xff1f;建议是LED灯更护眼。白炽灯缺点&#xff1a;耗电、发光效率低、温度过高不安全。白炽灯优点&#xff1a;体积小、显色能力好。LED灯缺点:价格较高、显色能力比白炽灯弱一些。LED灯优点&#xff1a…

JDBC(powernode CD2206)详尽版(内含教学视频、源代码、SQL文件)

JDBC&#xff08;powernode CD2206&#xff09;详尽版&#xff08;内含教学视频、源代码、SQL文件&#xff09; 包含&#xff1a;教学视频、源代码&#xff08;与博客同步&#xff09;、SQL文件 下载链接地址&#xff1a; https://download.csdn.net/download/weixin_4641135…

使用kubebuilder开发operator详解--踩坑记录

跟着教程&#xff1a;使用kubebuilder开发operator详解出现&#xff1a; 国内无法访问该ip&#xff0c;需要设置go env&#xff1a; go envGOPROXYhttps://goproxy.c 查看go env&#xff1a; 修改镜像后仍然无法解决&#xff1a;借鉴该问题https://github.com/goproxy/goprox…

springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源

springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater&#xff08;推荐-简单方便使用&#xff09;两种方式配置druid数据源druid数据源自定义配置druid数据源1.引入依赖2.配置自定义dataSoruce的Bean组件3.测试sql,验证数据源是否配置成功4.开启 StatFilter,wal…

哈希的应用 -- 布隆过滤器

作者&#xff1a;小萌新 专栏&#xff1a;C进阶 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;介绍并模拟实现哈希的应用 – 布隆过滤器 布隆过滤器布隆过滤器的提出布隆过滤器的概念布隆过滤器的实现框架与算法插入函数查找函数删…

JVM学习(五):JVM运行时参数

一、JVM参数选项1.1 标准参数选项标准参数选项的特点是以-开头&#xff0c;比较稳定&#xff0c;后续版本基本不会变化也就是在命令行输入java 或 java -help之后显示的参数&#xff0c;其中选项包括:-d32 使用 32 位数据模型 (如果可用)-d64 使用 64 位数据模型 (如果可用)-…

Spring Security in Action 第十章 SpringSecurity应用CSRF保护和CORS跨域请求

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;以实战为线索&#xff0c;逐步深入SpringSecurity相关知识相关知识&#xff0c;打造完整的SpringSecurity学习步骤&#xff0c;提升工程化编码能力和思维能力&#xff0c;写出高质量代码。希望大家都能够从中有所收获&#…

分布式链路追踪SkyWalking快速入门之环境安装界面指标介绍(一)

目录 一、先抛几个分布式常见的问题 二、分布式链路追踪Skywalking介绍 2.1 Skywalking是什么 2.2 市场上同类解决方案 2.3 skywalking的性能对比 三、Apache Skywalking特点和整体架构组件介绍 3.1 Skywalking特点 3.2 Skywalking整体架构 3.3 部署组件介绍 四.Apac…

HTML当中元素的id属性

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>HTML当中元素的id属性</title> </head> <body> <!-- 1、在HTML文档当中&#xff0c;任何元素/节…

详解promise与手写实现

详解promise与手写实现Promise1、Promise介绍与基本使用1.1 Promise概述1.2 Promise的作用1.3 Promise的使用2、Promise API3、Promise关键问题4、Promise自定义封装5、async与await5.1. mdn文档5.2.async函数5.3.await表达式5.4.注意Promise 1、Promise介绍与基本使用 1.1 P…

5.1 频率响应概述

一、研究放大电路频率响应的必要性 在放大电路中&#xff0c;由于电抗元件&#xff08;如电容、电感线圈等&#xff09;及半导体管极间电容的存在&#xff0c;当输入信号的频率过低或过高时&#xff0c;不但放大倍数的数值会变小&#xff0c;而且还将产生超前或者滞后的相移&a…