自定义 hooks 记录

记录一下常用的自定义 hook~~~

useForceUpdate

当我们使用React.PureComponent定义类组件的时候,因为React.PureComponent只是对数据做浅比较,当数据结构非常复杂的情况则可能出现数据更新页面不更新的情况,react 提供了一个forceupdate方法用来手动重新渲染组件,而函数组件是没有这个方法的,如果也想要实现手动刷新组件可以通过自定义 hook 的方法去实现;

以下记录了两种实现 useForceUpdate 自定义 hook 的方法:

useState 版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { useState, useCallback } from 'react'

/**
* 自定义hook 模拟类组件的forceUpdate
* 写法一 利用uesState
*/
const useForceUpdate = function (): Function {
const [, setCount] = useState < number > 0
const update = useCallback(() => {
setCount((prev: number): number => prev + 1)
}, [])

return update
}

export default useForceUpdate

useReducer 版

1
2
3
4
5
6
7
8
9
10
11
12
import { useReducer } from 'react'

/**
* 自定义hook 模拟类组件的forceUpdate
* 写法一 利用useReducer
*/
const useForceUpdate = function (): Function {
const [, forceUpdate] = useReducer((v: number): number => v + 1, 0)
return forceUpdate
}

export default useForceUpdate

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...

// 初始化updater
const updater = useForceUpdate()

...

// 调用刷新方法
const handleRefresh = ():void => {
updater()
}

...

return(
<div>
<button onClick={handleRefresh}>refresh</button>
</div>)

...

useClientRect

动态获取元素大小的自定义 hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

import {
useRef,
useState,
useCallback,
RefObject,
useLayoutEffect
} from 'react'
import debounce from 'lodash/debounce'

export const CLIENT_RECT_DEBOUNCE_INTERVAL = 200

/**
* 动态获取元素大小的自定义hook
* @param debounceInterval 防抖频率
* @param realtime
*/
function useClientRect<T extends HTMLElement>(
debounceInterval: number = CLIENT_RECT_DEBOUNCE_INTERVAL,
realtime: boolean = false
): [DOMRect, RefObject<T>] {

const ref = useRef<T>(null)
// @ts-ignore
const [rect, setRect] = useState<DOMRect>(null)

const resize = useCallback(
debounce(() => {
if (ref.current) {
setRect(ref.current.getBoundingClientRect())
}
}, debounceInterval),
[ref.current, realtime]
)

useLayoutEffect(() => {
if (!ref.current) {
return
}
resize()
// ResizeObserver 接口可以监听到 Element 的内容区域或 SVGElement的边界框改变
// 只会在绘制前或布局后触发调用
if (typeof ResizeObserver === 'function') {
// 开始观察指定的 Element或 SVGElement。
let resizeObserver = new ResizeObserver(resize)
resizeObserver.observe(ref.current)
return () => {
resizeObserver.disconnect()
// @ts-ignore
resizeObserver = null
}
} else {
window.addEventListener('resize', resize)
return () => {
window.removeEventListener('resize', resize)
}
}
}, [ref.current])

return [rect, ref]
}

export default useClientRect

// refs: https://github.com/rehooks/component-size/blob/master/index.js

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
// 自动计算元素的getBoundingClientRect值 demo
const [rect, refBackground] = useClientRect<HTMLDivElement>()

useEffect((): void => {
// 元素数据变化打印
console.log('rect',rect)
}, [rect])

...
return(
<div ref={refBackground} className={style.box}> 需要监听的元素 </div>
)
...