当前位置: 首页 > 科技观察

关于DOM的那些常见的Hook包,你知道几个?

时间:2023-03-14 12:25:24 科技观察

本文的主要目标如下:加深对Reacthooks的理解。了解如何抽象自定义挂钩。构建您自己的React钩子库。培养阅读和学习源码的习惯,工具库是阅读源码的好选择。useEventListener优雅地使用addEventListener。我们看一下addEventListener的定义。以下摘自MDN文档:EventTarget.addEventListener()方法将指定的监听器注册到EventTarget。当对象触发指定事件时,将执行指定的回调函数。这里的EventTarget可以是文档Element、Document和Window上的一个元素,也可以是任何其他支持事件的对象(比如XMLHttpRequest)。让我们看看useEventListener函数的TypeScript定义。通过类型重载,定义了Element、Document、Window等元素及其事件名称和回调参数。函数useEventListener(eventName:K,handler:(ev:HTMLElementEventMap[K])=>void,options?:Options,):void;functionuseEventListener(eventName:K,处理程序:(ev:ElementEventMap[K])=>void,options?:Options,):void;functionuseEventListener(eventName:K,处理程序:(ev:DocumentEventMap[K])=>void,options?:Options,):void;functionuseEventListener(eventName:K,handler:(ev:WindowEventMap[K])=>void,options?:Options,):无效;functionuseEventListener(eventName:string,handler:noop,options:Options):void;内部代码比较简单:判断是否支持addEventListener,支持则传参。几个参数的作用大家可以在评论中关注一下,作为复习,这里不再赘述。useEffect的返回逻辑,即组件卸载时,事件监听会自动清空,避免内存泄露。functionuseEventListener(//事件名称eventName:string,//处理函数handler:noop,//设置选项:Options={},){consthandlerRef=useLatest(handler);useEffectWithTarget(()=>{consttargetElement=getTargetElement(options.target,window);if(!targetElement?.addEventListener){return;}consteventListener=(event:Event)=>{returnhandlerRef.current(event);}};//监听事件targetElement.addEventListener(eventName,eventListener,{//该类型的事件捕获阶段传播到EventTarget时会触发监听器。capture:options.capture,//监听器会被调用atmostoncebeingadded.如果为true,监听器在调用后会自动移除once:options.once,//当设置为true时,表示监听器永远不会调用preventDefault()。如果侦听器仍然调用此函数,客户端将忽略它并抛出控制台警告passive:options.passive,});//移除事件return()=>{targetElement.removeEventListener(eventName,eventListener,{capture:options.capture,});};},[eventName,options.capture,options.once,options.passive],options.target,);}useClickAway监听目标元素外的点击事件。这里说的应用场景应该是模态框,点击外阴影部分,自动关闭场景。那么这里是如何实现的呢?首先,它支持传递DOM节点或Refs,它支持数组。该事件默认支持点击,开发者可以自行传递,支持数组方式。exportdefaultfunctionuseClickAway(//触发函数onClickAway:(event:T)=>void,//DOM节点或Ref,支持数组target:BasicTarget|BasicTarget[],//指定需要什么被监听的事件,支持数组eventName:string|string[]='click',){}内部通过document.addEventListener监听事件。卸载组件时清除事件侦听器。//事件列表consteventNames=Array.isArray(eventName)?eventName:[eventName];//document.addEventListener监听事件,通过事件代理知道目标节点eventNames.forEach((event)=>document.addEventListener(event,handler));return()=>{eventNames.forEach((event)=>document.removeEventListener(event,handler));};最后看handler函数,通过event.target(某个DOM元素)获取触发事件的对象,如果不在传入的target列表中,则触发定义的onClickAway函数。consthandler=(event:any)=>{consttargets=Array.isArray(target)?目标:[目标];if(//判断点击的DOMTarget是否在定义的DOM元素(列表)中;})){返回;}//触发点击事件onClickAwayRef.current(event);};小结暂时useClickAway使用事件代理的方式,通过文档监听事件,判断触发事件的DOM元素是否在目标列表中,从而决定是否触发定义的函数。useEventTarget封装了普通表单控件的onChange和value逻辑(通过e.target.value获取表单值),支持自定义值转换和重置功能。直接看代码比较简单。其实就是监听窗体的onChange事件,拿到值后更新值。更新后的逻辑支持自定义。函数useEventTarget(options?:Options){const{initialValue,transformer}=options||{};const[value,setValue]=useState(initialValue);//自定义转换函数consttransformerRef=useLatest(transformer);constreset=useCallback(()=>setValue(initialValue),[]);constonChange=useCallback((e:EventTarget)=>{//获取e.target.value并设置它const_value=e.target.value;if(isFunction(transformerRef.current)){returnsetValue(transformerRef.current(_value));}//没有转换器=>U和T应该相同returnsetValue(_valueasunknownasT);},[]);返回[值,{onChange,重置,},]为常量;}useTitle用于设置页面标题。此页面标题是指浏览器选项卡中显示的内容。通过document.title设置。代码很简单,看一下:useEffect(()=>{document.title=title;},[title]);useUnmount(()=>{//组件卸载后,恢复上次的标题if(options.restoreOnUnmount){document.title=titleRef.current;}});}useFavicon设置页面favicon.favicon指的是页面Tab的ICON。原理是通过link标签设置favicon。constuseFavicon=(href:string)=>{useEffect(()=>{if(!href)return;constcutUrl=href.split('.');constimgSuffix=cutUrl[cutUrl.length-1].toLocaleUpperCase()asImgTypes;constlink:HTMLLinkElement=document.querySelector("link[rel*='icon']")||document.createElement('link');//用于定义链接内容的类型。link.type=ImgTypeMap[imgSuffix];//指定链接资源的URLlink.href=href;//该属性命名链接文档和当前文档之间的关系。link.rel='快捷图标';document.getElementsByTagName('head')[0].appendChild(链接);},[href]);};