当前位置: 首页 > Web前端 > HTML

ahooks中控制“时机”的钩子是如何实现的?

时间:2023-03-27 23:40:33 HTML

本文是深入浅出ahooks源码系列文章的第五篇。本系列已整理成文档地址。觉得还不错,给个star支持一下,谢谢。本文探讨了ahooks是如何封装React的一些执行“时序”的?函数组件VS类组件学习React、Vue等框架,需要掌握它们的生命周期。我们需要清楚的知道我们代码的执行顺序,在不同阶段执行不同操作的代码,比如加载完成后需要挂起获取dom的值,否则可能获取不到对应的值。ClassComponent用过React的ClassComponent的同学都会知道,它的组件生命周期会分为三种状态:Mounting(挂载):插入到真实的DOMUpdating(更新):正在重新渲染Unmounting(卸载):从中移除real简单版的DOM如下:每个状态会依次调用不同的方法,对应的细节如下(这里不展开):可以通过官网查看细节,看到会有很多oflife的循环方式,并且在不同的版本中,生命周期的方式也是不同的。FunctionComponent当你来到FunctionComponent的时候,你会发现并没有直接提到生命周期的概念。它是一种更彻底的状态驱动。它只有一个状态,React负责将状态渲染到视图中。对于FunctionComponent来说,从state到页面渲染只有三步:输入state(prop,state)执行组件的逻辑,在useEffect/useLayoutEffect订阅sideeffectoutputUI(Dom节点)。重点在第二步。React通过useEffect/useLayoutEffect订阅副作用。ClassComponent中的生命周期可以通过useEffect/useLayoutEffect来实现。两者的功能很相似,这里看useEffect。使用useEffect相当于告诉React组件在渲染后需要执行某些动作,React会在执行DOM更新后调用它。React保证每次运行useEffect时,DOM都会更新。这就实现了ClassComponent中的Mounting(挂载阶段)。当状态发生变化时,它可以执行相应的逻辑,更新状态并将结果渲染到视图,从而完成类组件中的更新阶段。最后,通过在useEffect中返回一个函数,它清除了副作用。它的规则是:第一次渲染不清理,在下一次渲染时清理上一次的副作用。清理也在卸载阶段执行。通过返回一个函数,我们可以在类组件中实现卸载。ahooks在useEffect/useLayoutEffect的基础上做了一些封装,可以让你更清楚的知道你的代码什么时候执行。LifeCycle-useMount生命周期仅在组件初始化时执行。如果useEffect依赖为空,则只会在组件初始化时执行。//省略部分代码constuseMount=(fn:()=>void)=>{//省略部分代码//在useEffect(()=>{fn?.();},[]);};exportdefaultuseMount;useUnmountuseUnmount,组件卸载时执行的Hook。useEffect可以在组件渲染后实现各种副作用。可能需要清除一些副作用,因此您需要返回一个函数,该函数将在卸载组件时执行。constuseUnmount=(fn:()=>void)=>{constfnRef=useLatest(fn);useEffect(//Hook在组件卸载(unmount)时执行。//执行useEffect()返回值中的函数=>()=>{fnRef.current();},[],);};exportdefaultuseUnmount;useUnmountedRefHook获取当前组件是否已经卸载。通过判断useEffect中的返回值是否已经执行,判断当前组件是否已经卸载。//Hook获取当前组件是否已经卸载。constuseUnmountedRef=()=>{constunmountedRef=useRef(false);useEffect(()=>{unmountedRef.current=false;//如果已经卸载,则执行return中的逻辑return()=>{unmountedRef.current=true;};},[]);returnunmountedRef;};exportdefaultuseUnmountedRef;Effect这里我们只讲下面官方文档中的Effect,其中有一些是定时器,防抖节流等,我们下面系列具体分析。useUpdateEffect和useUpdateLayoutEffectuseUpdateEffect和useUpdateLayoutEffect的用法与useEffect和useLayoutEffect相同,只是第一次执行会被忽略,只在依赖更新时才执行。实现思路:初始化一个标识符,一开始为false。第一次执行完成时设置为true。仅当标识符为真时才执行回调函数。//忽略第一次执行exportconstcreateUpdateEffect:(hook:effectHookType)=>effectHookType=(hook)=>(effect,deps)=>{constisMounted=useRef(false);//对于react-refreshhook(()=>{return()=>{isMounted.current=false;};},[]);hook(()=>{//第一次执行后,设置为true,这样下次更新依赖时可以执行逻辑if(!isMounted.current){isMounted.current=true;}else{返回效果();}},部门);};useDeepCompareEffect和useDeepCompareLayoutEffect的使用方法和useEffect一样,只是deps通过lodashisEqual进行深度比较。使用useRef保存上一次依赖的值,与当前依赖进行比较(使用lodash的isEqual),将比较结果作为useEffect的依赖判断是否执行回调函数。constdepsEqual=(aDeps:DependencyList,bDeps:DependencyList=[])=>{returnisEqual(aDeps,bDeps);};constuseDeepCompareEffect=(effect:EffectCallback,deps:DependencyList)=>{//保存最后一次useRefconstref=useRef();constsignalRef=useRef(0);//判断最新依赖和旧依赖的区别//如果相等,改变signalRef.current触发useEffect回调if(!depsEqual(deps,ref.current)){ref.current=deps;signalRef.current+=1;}useEffect(effect,[signalRef.current]);};useUpdateuseUpdate会返回一个函数,调用该函数会强制组件重新渲染。返回的函数导致组件通过改变useState返回的状态来更新。从“反应”导入{useCallback,useState};constuseUpdate=()=>{const[,setState]=useState({});//通过设置一个新的状态使函数组件更新组件的生命周期就是,我们的代码的执行顺序和时机是怎样的。在FunctionComponent中,使用useEffect/useLayoutEffect来完成ClassComponents的生命周期职责。ahooks也是基于这两个封装了常用代码执行机会的包。使用这些钩子可以让我们的代码更易读,逻辑更清晰。