对于使用react的同学来说,hooks一定不陌生,但是如何封装hooks以及如何在业务中使用封装的hooks,很多同学都没有很好的实践,本文将通过10个常用的hooks让大家学习封装hooks,可以用于自己的业务,提高复用率,降低开发成本。边界钩子的作用是什么?它可以让你封装一些功能组件的重复逻辑,将组件分离并细化它们的功能,让组件逻辑简单明了,让逻辑共享更容易,减少代码重复,让维护和更新更简单简单易懂。hook的本质是让我们的组件不再使用class组件,所以如果你的项目还在使用react的class组件,就不能使用hooks了。React也有一些对应的内置hook,比如我们常用的useState、useEffect等,下面开始封装我们自己的hook库。useToggleimport{useState}from"react"exportdefaultfunctionuseToggle(defaultValue){const[value,setValue]=useState(defaultValue)functiontoggleValue(value){setValue(currentValue=>typeofvalue==="boolean"?value:!currentValue)}return[value,toggleValue]}通过代码可以看出这个hook的作用,本质就是切换状态,你可以理解为一个react组件,也可以理解为一个函数。这个函数接受一个初始值,用useState存储状态,通过函数toggleValue切换状态,然后函数返回两个内容,一个是当前值,一个是toggleValue函数,切换状态,但是组件返回一段jsx代码,这里返回的是一个数组,在使用上变得非常简单。导出默认函数ToggleComponent(){const[值,toggleValue]=useToggle(false)return(
{value.toString()}
ToggletoggleValue(true)}>MakeTruetoggleValue(false)}>MakeFalse)}useStorage前端数据存储离不开localStorage和sessionStorage,如何根据这个内容写一个自定义的hook?import{useCallback,useState,useEffect}from"react"exportfunctionuseLocalStorage(key,defaultValue){returnuseStorage(key,defaultValue,window.localStorage)}exportfunctionuseSessionStorage(key,defaultValue){返回useStorage(key,defaultValue,window.sessionStorage)}functionuseStorage(key,defaultValue,storageObject){const[value,setValue]=useState(()=>{constjsonValue=storageObject.getItem(key)if(jsonValue!=null)返回JSON.parse(jsonValue)if(typeofdefaultValue==="function"){returndefaultValue()}else{returndefaultValue}})useEffect(()=>{if(value===undefined)returnstorageObject.removeItem(key)storageObject.setItem(key,JSON.stringify(value))},[key,value,storageObject])constremove=useCallback(()=>{setValue(undefined)},[])return[value,setValue,remove]}这两个钩子功能类似,接收两个一个参数,key和defaultValue,当然你也可以扩展过期时间相关的内容useEffect监听key或value是否变化并进行一系列操作,通过JSON.stringify格式化成字符串,执行删除操作根据值是否未定义SimpleexportdefaultfunctionStorageComponent(){const[age,setAge,removeAge]=useLocalStorage("age",26)return(
{name}-{age}
setAge(40)}>设置年龄删除年龄)}useAsyncimport{useCallback,useE效果,useState}来自“反应”t;导出默认函数useAsync(callback,dependencies=[]){const[loading,setLoading]=useState(true)const[error,setError]=useState()const[value,setValue]=useState()constcallbackMemoized=useCallback(()=>{setLoading(true)setError(undefined)setValue(undefined)callback().then(setValue).catch(setError).finally(()=>setLoading(false))},dependencies)useEffect(()=>{callbackMemoized()},[callbackMemoized])return{loading,error,value}}主要内容还是对useState和useEffect的封装,它们结合起来形成对useAsync的封装。callback传入一个Promise函数,将Loading、error、value统一处理,并为useEffect的执行时机加入依赖参数const{loading,error,value}=useAsync(()=>{returnnewPromise((resolve,reject)=>{constsuccess=falsesetTimeout(()=>{success?resolve("Hi"):reject("Error")},1000)})})useFetch根据我们封装的useAsync,通过进一步处理,我们还可以得到更好的useFetch,之后在项目中使用时不需要使用自己打包的fetch.js。毕竟,状态中没有加载或值绑定操作。您可以使用更有用的useFetchconstDEFAULT_OPTIONS={headers:{"Content-Type":"application/json"},}exportdefaultfunctionuseFetch(url,options={},dependencies=[]){returnuseAsync(()=>{returnfetch(url,{...DEFAULT_OPTIONS,...options}).then(res=>{if(res.status===200)returnres.datareturnPromise.reject(res)})},dependencies)}用法const{loading,error,value}=useFetch(url,{method:'post'})useEffectOnce实现起来比较简单import{useEffect}from"react"exportdefaultfunctionuseEffectOnce(cb){useEffect(cb,[])}使用相同的useEffectOnce(()=>alert("Hi"))useRenderCount查看一个页面被渲染了多少次import{useEffect,useRef}from"react"exportdefaultfunctionuseRenderCount(){constcount=useRef(1)useEffect(()=>count.current++)returncount.current}使用constrenderCount=useRenderCount()useTimeoutimport{useCallback,useEffect,useRef}从“react”导出默认函数useTimeout(callback,delay){constcallbackRef=useRef(callback)consttimeoutRef=useRef()useEffect(()=>{callbackRef.current=callback},[回调])constset=useCallback(()=>{timeoutRef.current=setTimeout(()=>callbackRef.current(),delay)},[delay])constclear=useCallback(()=>{timeoutRef.current&&clearTimeout(timeoutRef.current)},[])useEffect(()=>{set()returnclear},[delay,set,clear])constreset=useCallback(()=>{clear()set()},[clear,set])return{reset,clear}}这个hook的本质是长时间延迟回调函数的执行,对外暴露了两个方法,分别是reset重置和clear清除定时器,可以更方便的操作定时器,使用ref保存定时器和回调函数。使用const{clear,reset}=useTimeout(()=>setCount(0),1000)通过点击按钮或调用函数useDebounce来操作定时器是一样的,进一步封装useTimeout可以实现debounce的操作。主要目的是解决在指定时间内重复调用某个方法,可以通过hook轻松解决这种问题exportdefaultfunctionuseDebounce(callback,delay,dependencies){const{reset,clear}=useTimeout(callback,delay)useEffect(reset,[...dependencies,reset])useEffect(clear,[])}其中可以通过改变依赖关系来控制reset,可以控制执行的频率const[count,setCount]=useState(10)useDebounce(()=>alert(count),1000,[count])如果count在1s内频繁变化,是不会触发alert的。当然,也可以通过是否立即执行这个参数来进行一些相应的控制。这里我就不提了。感兴趣的同学可以自行完善。总的来说,封装hooks还是比较简单的,可以理解为将一些常用的nativehooks或者一些函数重新封装起来,结合state或者effect提取一些通用的逻辑,让页面的改动更简单,更专注于的逻辑页面本身,也需要注意一些使用hooks的规则,本质上就是一个js函数只能在函数的最外层调用hooks,不能在循环、条件判断、子函数中调用。它只能在React函数组件中调用钩子,不能在其他JavaScript函数中调用。当然你也可以自定义hooks可以在自定义函数中调用,比如我们实现的useFetch就是基于useAsync