
不会封装hook?看下ahooks这6个hook是怎么做的
不会封装hook?看下ahooks这6个hook是怎么做的
1. useUpdate
在React函数组件中如何强制组件进行刷新?虽然React没有提供原生的方法,但当state值变化时,React函数组件会刷新。useUpdate
就是利用了这一点,源码如下:
import { useCallback, useState } from 'react'; |
useUpdate
的返回值函数,就是每次都用一个新对象调用setState
,触发组件的更新。
2. useMount
React函数组件没有了mount的生命周期,但有时我们需要在组件第一次渲染之后执行一次操作。可以封装useEffect
实现这个需求,只需要把依赖项设置成空数组,那么就只在渲染结束后执行一次回调:
import { useEffect } from 'react'; |
3. useLatest
React函数组件是一个可中断、可重复执行的函数,每次state
或props
变化时,函数都会重新执行。函数的作用域是创建函数时就固定下来的,如果其中的内部函数不更新,那么这些函数获取到的外部变量就不会变。例如:
import React, { useState, useEffect } from 'react'; |
这是一个定时更新count
值的例子,但上面的代码只会让count
一直是1,因为setInterval
中的函数在创建时,它的作用域就定下来了,它拿到的count
永远是0。当执行了setCount
后,会触发函数的重新执行,重新执行时,虽然count
值变成了1,但这个count
已经不是它作用域上的count
变量了。函数的每次执行都会创建新的环境,而useState
、useRef
等这些hooks提供了函数重新执行后保持状态的能力,但对于那些没有重新创建的函数,它们的作用域就永远停留在了创建的时刻。
如何让count
正确更新?一个简单直接的方法是在setCount
的同时,也直接更新count
变量,即直接改变闭包变量的值,这在JS中是允许的:
import React, { useState, useEffect } from 'react'; |
setCount
是为了让函数刷新,并更新函数的count
值,而直接给count
赋值是为了更新定时任务函数中维护的闭包变量。但这显然不是一个好的解决办法,更好的办法是让定时任务函数能够拿到函数最新的count
值。useState
返回的count
每次都是新的变量,变量地址是不同的,应该让定时任务函数引用一个变量地址不变的对象,这个对象中再记录最新的count
值,而实现这个功能就需要用到useRef
,它能帮助我们在每次函数刷新时都返回相同变量地址的对象,实现方式如下:
import React, { useState, useEffect, useRef } from 'react'; |
可以看到定时函数获取的latestCount
永远是定义时的变量,但因为useRef
,每次函数执行时它的变量地址不变,并且还把count
的最新值赋值给了latestCount.current
,定时函数就可以获取到最新的count
值。所以这个功能可以封装成useLatest
,获取最新值的功能:
import { useRef } from 'react'; |
上面的例子是为了说明useLatest
的作用,但针对这个例子,只是为了给count+1
,还可以通过setCount
方法本身获取,虽然定时任务函数中的setCount
一直是最初定义的函数,但它的功能可以通过传递函数的方式获取到最新的count
值,代码如下:
const [count, setCount] = useState(0); |
4. useUnmount
有了useMount
就会有useUnmount
,利用的是useEffect
的函数会返回一个cleanup函数,这个函数在组件卸载和useEffect
的依赖项变化时触发。通常情况下,我们在useEffect
中做了什么操作,返回的cleanup函数就应该进行相应的清除,例如useEffect
创建定时器,那么返回的cleanup函数就应该清除定时器:
const [count, setCount] = useState(0); |
所以useUnmount
就是利用了这个cleanup函数实现useUnmount
的能力,代码如下:
import { useEffect } from 'react'; |
使用了useLatest
存放fn
的最新值,写了一个空的useEffect
,依赖是空数组,只在函数卸载时执行。
5. useToggle和useBoolean
useToggle
封装了可以让state
在两个值之间变化的功能,useBoolean
则是利用了useToggle
,固定两个值只能是true
和false
。看看它们的源码:
function useToggle(defaultValue = false, reverseValue) { |
调用useToggle
时可以设置初始值和相反值,默认初始值是false
,actions
用useMemo
封装是为了提高性能,避免每次渲染都重新创建这些函数。setLeft
是设置初始值,setRight
是设置相反值,set
是用户随意设置,toggle
是切换两个值。useBoolean
则是在useToggle
的基础上进行了封装,让我们用起来更加简洁方便:
export default function useBoolean(defaultValue = false) { |
总结
本文介绍了ahooks
中封装的6个简单的hook,虽然简单,但可以通过它们的做法,学习到自定义hook的思路和作用,即把一些能够重用的逻辑封装起来。在实际项目中,有这个意识就可以封装出适合项目的hook。