当前位置: 首页 > java >正文

React自定义Hook之useMutilpleRef

概要

我们在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.xdnf.cn/news/2391.html

相关文章:

  • CD33.【C++ Dev】初识模版
  • 深度学习4.1 多层感知机
  • 基础的贝叶斯神经网络(BNN)回归
  • 【C++详解】C++入门(二)引用、内联函数、nullptr宏
  • 23种设计模式-行为型模式之备忘录模式(Java版本)
  • MSO-Player:基于vlc的Unity直播流播放器,支持主流RTSP、RTMP、HTTP等常见格式
  • Python包的编译、构建与打包指南
  • 解析 OpenHarmony、HarmonyOS 与 HarmonyOS Next:优雅草卓伊凡的观点
  • AI医疗革命:DeepMind CEO展望十年内攻克疾病难题
  • 权力结构下的人才价值重构:从 “工具论” 到 “存在论” 的转变​
  • 《深入浅出Git:从版本控制原理到高效协作实战》​
  • AOSP Android14 Launcher3——Launcher的状态介绍LauncherState类
  • 文章记单词 | 第49篇(六级)
  • 20250427 对话1: 何东山的宇宙起源理论
  • Java学习-Java基础
  • JavaEE-多线程实战01
  • VScode在 Markdown 编辑器中预览
  • err: Error: Request failed with status code 400
  • 大模型——Spring.new快速构建AI驱动的定制化商业应用
  • 在线教育系统开发常见问题及解决方案:源码部署到运营维护
  • 关系型数据库PostgreSQL for Mac 保姆级使用教程
  • iOS自定义电池电量显示控件 BatteryView 实现
  • 【Java】分布式事务解决方案
  • 【Axure高保真原型】3级多选下拉列表
  • 统信操作系统使用默认yum源安装 Docker 的踩坑
  • 基于 Playwright 构建小型分布式爬虫(进阶版)
  • 关于指针和指针算术
  • [论文梳理] 足式机器人规划控制流程 - 接触碰撞的控制 - 模型误差 - 自动驾驶车的安全合规(4个课堂讨论问题)
  • 误触网络重置,笔记本电脑wifi连接不上解决方法(Win10,Win11通用)
  • JS-OCR-demo加载本地文件