React自定义Hook之useMutilpleRef

news2025/7/16 6:11:26

概要

我们在React开发时候,有时候需要绑定列表中的多个元素,便于后面对列表中单个元素的操作,但是常用的hook函数useRef只能绑定一个DOM元素,本文提供一个可以解决该问题的自定义hook方法,useMutilpleRef。

代码及实现

基本功能

本例使用Map来缓存DOM元素的引用,如果DOM元素存在,则缓存在Map中;如果DOM元素被删除,则在Map中对应的键值对也就被删除。

代码实现

export const useMutipleRefs = <T extends HTMLElement>() => {
    const refs = useRef<Map<string|number, T> |null>(new Map<string|number, T>());
    const setRef = (key:string) => (el:T | null) => {
        if (el) {
            refs.current!.set(key, el);
        }else {
            refs.current!.delete(key);
        }
    } 

    useEffect(()=>{
        return ()=> {refs.current = null};
    }, []);

    return {refs, setRef};
}
  1. 该方法不用接受任何参数,采用泛型约束,只处理DOM元素;
  2. 采用Map来缓存DOM元素,key值可以是字符串,以支持React 18的useId hook函数,也可以是普通数字;
  3. 该方法的返回值包括两部分,首先是绑定了DOM元素引用的Map,该Map的内容可以被迭代查询,以支持各种复杂的应用场景;
  4. 返回值的另一部分是setRef 方法,该方法在绑定元素的时候被调用,参数可以接受HTMLElement和空值,空值用于删除的情况;
  5. 在组件被卸载时候,释放资源。

使用实例

下面是一个人员列表的例子,可以通过性别过滤列表中的元素,每个元素需要支持删除操作。
在这里插入图片描述

关键代码如下, 完整代码请见附录:

响应式数据定义如下:

const [gender, setGender] = useState<Gender>(Gender.Female);
const [filteredIds, setFilteredIds] = useState<Array<number|string>>([]);
const userUI = useMemo(()=> users.filter(u=> u.gender === gender && !filteredIds.includes(u.id)), [gender, filteredIds]);

监听内容如下:

  1. 对当前选中的性别进行监听;
  2. 对要删除的User进行监听;
  3. 对列表内容进行缓存,只有选中的性别和要删除的Id数组发生变化,它才变化。

主要的点击事件代码如下:

type funcType = (event:MouseEvent)=>void;
const handleClickGender = (gender:Gender):funcType=>(event:MouseEvent)=> {
     setGender(gender);
} 
const clickDelete = (index:number|string)=> {
  console.log(index);
  setFilteredIds(prev=>[...prev, index]);
}

  1. 性别过滤按钮点击时候,触发handleClickGender
  2. 删除按钮点击时候,触发clickDelete

Refs 相关的代码

const {refs, setRef} = useMutipleRefs();
useEffect(()=> {
    console.log(refs.current); 
}, [userUI])

 <tbody>
 {
      userUI.map((user, index)=>(
          <tr key={user.id} ref={setRef(user.id)}>
              <td>{index + 1}</td>
              <td>{user.name}</td>
              <td>{user.gender === Gender.Female ? "Female": "Male"}</td>
              <td>{user.married? "Yes": "No"}</td>
              <td><button className="btn btn-success" onClick={()=>clickDelete(user.id)}>Delete</button></td>
          </tr>
      ))
  }
</tbody>
  1. 调用useMutipleRefs过的初始的refs的Map对象和setRef方法
  2. 对表格中的每个tr,调用setRef,传入用户Id

代码运行

在这里插入图片描述
程序默认是获取所有Female的User信息,因此初始时候,有三个User的引用

点击Male按钮,显示有两个Male的User

在这里插入图片描述
Map中的数据更新为所有Male的Tr引用。
在这里插入图片描述

点击删除第一个Delete按钮
在这里插入图片描述
数据成功删除

在这里插入图片描述
Map中缓存的DOM引用,也同时被删除
在这里插入图片描述

使用中额外注意的问题

本文提供的缓存方法,监听的DOM对象的类型是HTMLElement,它是DOM元素的基类,如果要使用其它DOM的特有属性,请在调用时候,明确具体类型。

例如存的DOM元素是input,并且需要在后面使用input的value,在调用该方法是,请明确Input的的类型,useMutipleRefs(),否则无获取不到其value值。

结论

本文提供的自动Hook useMutipleRefs可以实现对列表中多个元素引用值的缓存,在DOM元素在创建时候,可以同步创建,删除时候可以同步删除。

附录

完整代码

"use client"
import { FC, useEffect, useMemo, useState } from "react";
import { ReactElement, MouseEvent,useId } from "react";
import { usePrevious, useMutipleRefs } from "../hooks";
import "@/app/assets/bootstrap.min.css"
interface IProps{

}

enum  Gender {
    Male=0,
    Female
}

interface IUser {
    id:number|string;
    name:string;
    gender:Gender;
    married:boolean;
}

const Team:FC<IProps> = ({}):ReactElement => {
    const users = [
        {
            id:useId(),
            name:"abc",
            gender:Gender.Female,
            married:false
        },
        {
            id:useId(),
            name:"def",
            gender:Gender.Male,
            married:false
        },
        {
            id:useId(),
            name:"ghi",
            gender:Gender.Female,
            married:true
        },
        {
            id:useId(),
            name:"jkl",
            gender:Gender.Male,
            married:false
        },
        {
            id:useId(),
            name:"mno",
            gender:Gender.Female,
            married:true
        }
    ] as Array<IUser>;


        
const [gender, setGender] = useState<Gender>(Gender.Female);

type funcType = (event:MouseEvent)=>void;
const handleClickGender = (gender:Gender):funcType=>(event:MouseEvent)=> 					{
    setGender(gender);
} 


const [filteredIds, setFilteredIds] = useState<Array<number|string>>([]); 
const userUI = useMemo(()=> users.filter(u=> u.gender === gender && !filteredIds.includes(u.id)), [gender, filteredIds]);
 
 
 const {refs, setRef} = useMutipleRefs();
 const clickDelete = (index:number|string)=> {
     console.log(index);
     setFilteredIds(prev=>[...prev, index]);
 }
 useEffect(()=> {
     console.log(refs.current); 
 }, [userUI])
        return <>
                <div className="btn-group" role="group" aria-label="Basic outlined example">
                    <button className="btn btn-primary" onClick={handleClickGender(Gender.Female)}>Female</button>
                    <button className="btn btn-warning" onClick={handleClickGender(Gender.Male)}>Male</button>
                </div>
                <table className="table table-striped" style={{width:"50%"}}>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Name</th>
                            <th>Gender</th>
                            <th>Married</th>
                            <th>Operation</th>
                        </tr>    
                    </thead>
                    <tbody>
                        {
                            userUI.map((user, index)=>(
                                <tr key={user.id} ref={setRef(user.id)}>
                                    <td>{index + 1}</td>
                                    <td>{user.name}</td>
                                    <td>{user.gender === Gender.Female ? "Female": "Male"}</td>
                                    <td>{user.married? "Yes": "No"}</td>
                                    <td><button className="btn btn-success" onClick={()=>clickDelete(user.id)}>Delete</button></td>
                                </tr>
                            ))
                        }
                    </tbody>
                </table>
            </>
}

export default Team;

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

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

相关文章

从线性到非线性:简单聊聊神经网络的常见三大激活函数

大家好&#xff0c;我是沛哥儿&#xff0c;我们今天一起来学习下神经网络的三个常用的激活函数。 引言&#xff1a;什么是激活函数 激活函数是神经网络中非常重要的组成部分&#xff0c;它引入了非线性因素&#xff0c;使得神经网络能够学习和表示复杂的函数关系。 在神经网络…

网络准入控制系统推荐:2025年构建企业网络安全的第一道防线

随着信息技术的飞速发展&#xff0c;企业网络环境日益复杂&#xff0c;阳途网络准入控制系统作为一种先进的网络安全解决方案&#xff0c;其核心是确保网络接入的安全性。 一、网络准入控制系统的基本原理与功能 网络准入控制以“只有合法的用户、安全的终端才可以接入网络”为…

XSS跨站--订单和Shell箱子后门

本文主要内容 手法 XSS平台使用 XSS工具使用 XSS结合其他漏洞 XSS具体使用场景 某订单系统XSS盲打_平台 某Shell箱子系统XSS盲打_工具 [1]订单系统经典案例 第一个简易攻击流程&#xff08;订单系统&#xff09;&#xff1a;通过平台完成XSS跨站之后&a…

游戏遭遇DDoS攻击如何快速止损?实战防御策略与应急响应指南

是不是很抽象 我自己画的 一、游戏DDoS攻击特征深度解析 游戏行业DDoS攻击呈现复合型特征&#xff0c;2023年监测数据显示&#xff0c;针对游戏服务器的攻击中&#xff0c;63%采用UDP反射放大HTTP慢速攻击组合&#xff0c;攻击峰值达3.2Tbps。攻击者利用游戏协议特性&#xff…

cocos creator使用jenkins打包流程,打包webmobile

windows电脑使用 如果你的电脑作为打包机&#xff0c;一定要锁定自己的ip,如果ip动态获取&#xff0c;可能后续会导致jenkins无法访问,还需要重新配置jenkins和http-server的端口 从jenkins官网下载windows版 Thank you for downloading Windows Stable installer 1.jenkins安…

自动驾驶(ADAS)领域常用数据集介绍

1. KITTI 数据集 简介&#xff1a;由德国卡尔斯鲁厄理工学院与丰田研究院联合创建&#xff0c;是自动驾驶领域最经典的评测基准&#xff0c;涵盖立体视觉、光流、3D检测等任务。包含市区、乡村和高速公路场景的真实数据&#xff0c;标注对象包括车辆、行人等&#xff0c;支持多…

关于IDEA的循环依赖问题

bug描述&#xff1a;&#xff08;java: 模块循环不支持注解处理。请确保将循环 [...] 中的所有模块排除在注解处理之外&#xff09; 解决方法&#xff1a;

如何在idea中写spark程序

在 IntelliJ IDEA 中编写 Spark 程序&#xff0c;可按以下步骤进行&#xff1a; 1. 创建新项目 打开 IntelliJ IDEA&#xff0c;选择File -> New -> Project。在左侧面板选择Maven或者Gradle&#xff08;这里以 Maven 为例&#xff09;&#xff0c;确保Project SDK选择…

RAG工程-基于LangChain 实现 Advanced RAG(预检索优化)

Advanced RAG 概述 Advanced RAG 被誉为 RAG 的第二范式&#xff0c;它是在 Naive RAG 基础上发展起来的检索增强生成架构&#xff0c;旨在解决 Naive RAG 存在的一些问题&#xff0c;如召回率低、组装 prompt 时的冗余和重复以及灵活性不足等。它重点聚焦在检索增强&#xff0…

《Masked Autoencoders Are Scalable Vision Learners》---CV版的BERT

目录 一、与之前阅读文章的关系&#xff1f; 二、标题&#xff1a;带掩码的自auto编码器是一个可拓展的视觉学习器 三、摘要 四、核心图 五、结果图 六、不同mask比例对比图 七、“Introduction” (He 等, 2021, p. 1) 引言 八、“Related Work” (He 等, 2021, p. 3)相…

高压直流输电MATLAB/simulink仿真模型+说明文档

1.模型简介 本仿真模型基于MATLAB/Simulink&#xff08;版本MATLAB 2018Ra&#xff09;软件。建议采用matlab2018 Ra及以上版本打开。&#xff08;若需要其他版本可联系代为转换&#xff09; 使用一个传输功率为1000MW&#xff08;500 kV&#xff0c;2 kA&#xff09;直流互连…

locust压力测试

安装 pip install locust验证是否安装成功 locust -V使用 网上的教程基本上是前几年的&#xff0c;locust已经更新了好几个版本&#xff0c;有点过时了&#xff0c;在此做一个总结 启动 默认是使用浏览器进行设置的 # 使用浏览器 locust -f .\main.py其他参数 Usage: locust […

第十二届蓝桥杯 2021 C/C++组 空间

目录 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 思路&#xff1a; 思路详解&#xff1a; 代码&#xff1a; 代码详解&#xff1a; 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 空间 - 蓝桥云课 思路&#xff1a; 思路详解&#…

以太网的mac帧格式

一.以太网的mac帧 帧的要求 1.长度 2.物理层

业绩回暖、股价承压,三只松鼠赴港上市能否重构价值锚点?

在营收重返百亿俱乐部后&#xff0c;三只松鼠再度向资本市场发起冲击。 4月25日&#xff0c;这家坚果零食巨头正式向港交所递交上市申请书&#xff0c;若成功登陆港股&#xff0c;将成为国内首个实现“AH”双上市的零食品牌。 其赴港背后的支撑力&#xff0c;显然来自近期披露…

JAVA-StringBuilder使用方法

JAVA-StringBuilder使用方法 常用方法 append(Object obj) 追加内容到末尾 sb.append(" World"); insert(int offset, Object obj) 在指定位置插入内容 sb.insert(5, “Java”); delete(int start, int end) 删除指定范围的字符 sb.delete(0, 5); replace(int start…

【Python】Matplotlib:立体永生花绘制

本文代码部分实现参考自CSDN博客&#xff1a;https://blog.csdn.net/ak_bingbing/article/details/135852038 一、引言 Matplotlib作为Python生态中最著名的可视化库&#xff0c;其三维绘图功能可以创造出令人惊叹的数学艺术。本文将通过一个独特的参数方程&#xff0c;结合极…

Unity AI-使用Ollama本地大语言模型运行框架运行本地Deepseek等模型实现聊天对话(一)

一、Ollama介绍 官方网页&#xff1a;Ollama官方网址 中文文档参考&#xff1a;Ollama中文文档 相关教程&#xff1a;Ollama教程 Ollama 是一个开源的工具&#xff0c;旨在简化大型语言模型&#xff08;LLM&#xff09;在本地计算机上的运行和管理。它允许用户无需复杂的配置…

SAP /SDF/SMON配置错误会导致HANA OOM以及Disk Full的情况

一般来说&#xff0c;为了保障每日信息收集&#xff0c;每个企业都会配置/SDF/SMON的监控。这样在出现性能问题时&#xff0c;可以通过收集到的snapshot进行分析检查。如果/SDF/SMON在配置时选取了过多的记录项&#xff0c;或者选择了过低的时间间隔[Interval in seconds],那显…

CMU和苹果公司合作研究机器人长序列操作任务,提出ManipGen

我们今天来介绍一项完成Long-horizon任务的一项新的技术&#xff1a;ManipGen。 什么叫Long-horizon&#xff1f;就是任务比较长。说到底&#xff0c;也是任务比较复杂。 那么这个技术就给我们提供了一个非常好的解决这类问题的思路&#xff0c;同时&#xff0c;也取得了不错的…