Closed hyeyoonS closed 2 months ago
usehooks-ts 라이브러리 문서에 있는 useResizeObserver 커스텀 훅을 가져왔습니다.
[목적] 요소의 크기 변경을 감지하여, onResize 콜백을 실행시키고 현재 크기를 반환합니다.
[작동 원리]
[타입 원리]
[코드]
import { useEffect, useRef, useState } from 'react'
import type { RefObject } from 'react'
import { useIsMounted } from 'usehooks-ts'
type Size = {
width: number | undefined
height: number | undefined
}
type UseResizeObserverOptions<T extends HTMLElement = HTMLElement> = {
ref: RefObject<T>
onResize?: (size: Size) => void
box?: 'border-box' | 'content-box' | 'device-pixel-content-box'
}
const initialSize: Size = {
width: undefined,
height: undefined,
}
export function useResizeObserver<T extends HTMLElement = HTMLElement>(
options: UseResizeObserverOptions<T>,
): Size {
const { ref, box = 'content-box' } = options
const [{ width, height }, setSize] = useState<Size>(initialSize)
const isMounted = useIsMounted()
const previousSize = useRef<Size>({ ...initialSize })
const onResize = useRef<((size: Size) => void) | undefined>(undefined)
onResize.current = options.onResize
useEffect(() => {
if (!ref.current) return
if (typeof window === 'undefined' || !('ResizeObserver' in window)) return
const observer = new ResizeObserver(([entry]) => {
const boxProp =
box === 'border-box'
? 'borderBoxSize'
: box === 'device-pixel-content-box'
? 'devicePixelContentBoxSize'
: 'contentBoxSize'
const newWidth = extractSize(entry, boxProp, 'inlineSize')
const newHeight = extractSize(entry, boxProp, 'blockSize')
const hasChanged =
previousSize.current.width !== newWidth ||
previousSize.current.height !== newHeight
if (hasChanged) {
const newSize: Size = { width: newWidth, height: newHeight }
previousSize.current.width = newWidth
previousSize.current.height = newHeight
if (onResize.current) {
onResize.current(newSize)
} else {
if (isMounted()) {
setSize(newSize)
}
}
}
})
observer.observe(ref.current, { box })
return () => {
observer.disconnect()
}
}, [box, ref, isMounted])
return { width, height }
}
type BoxSizesKey = keyof Pick<
ResizeObserverEntry,
'borderBoxSize' | 'contentBoxSize' | 'devicePixelContentBoxSize'
>
function extractSize(
entry: ResizeObserverEntry,
box: BoxSizesKey,
sizeType: keyof ResizeObserverSize,
): number | undefined {
if (!entry[box]) {
if (box === 'contentBoxSize') {
return entry.contentRect[sizeType === 'inlineSize' ? 'width' : 'height']
}
return undefined
}
return Array.isArray(entry[box])
? entry[box][0][sizeType]
: // @ts-ignore Support Firefox's non-standard behavior
(entry[box][sizeType] as number)
}
❓문제
1) 유용하게 사용 중인, 혹은 새롭게 알게 된 커스텀 훅을 알려주세요. 2) 해당 훅에 만약 타입에 대한 설정이 있다면 같이 알려주세요!
🎯답변
usehooks-ts 라이브러리 문서에 있는 useResizeObserver 커스텀 훅을 가져왔습니다.
[목적] 요소의 크기 변경을 감지하여, onResize 콜백을 실행시키고 현재 크기를 반환합니다.
[작동 원리]
[타입 원리]
[코드]