实现精简版useSize钩子函数
ahooks是阿里开源的一套React Hooks库,里面封装了大量好用的Hooks,最近也在项目中频繁使用到了库中useSize这个钩子函数,于是就学习了一下它的源码实现,并且实现了一个精简版useSize。
要点一:ResizeObserver监听Element内容区域的边界框改变
要点二:requestAnimationFrame优化高频刷新情况下的数据更新
代码实现
import {
MutableRefObject,
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState
} from "react";
type TargetValue<T> = T | undefined | null;
type TargetType = HTMLElement | Element | Window | Document;
export type BasicTarget<T extends TargetType = Element> =
| (() => TargetValue<T>)
| TargetValue<T>
| MutableRefObject<TargetValue<T>>;
type Size = { width: number, height: number }
export default function useSize(target: BasicTarget): Size | undefined {
const [state, setState] = useState<Size | undefined>();
const ref = useRef(0);
const [resizeObserver, setResizeObserver] = useState<ResizeObserver>();
const setRafState = useCallback((value: Size | ((prevState?: Size) => Size)) => {
cancelAnimationFrame(ref.current);
ref.current = requestAnimationFrame(() => {
setState(value)
})
}, [])
useEffect(() => () => {
resizeObserver?.disconnect();
cancelAnimationFrame(ref.current);
}, [])
useLayoutEffect(() => {
if (!target) return;
let targetEl: TargetValue<TargetType>;
if ('current' in target) {
targetEl = target.current;
} else if (typeof target === "function") {
targetEl = target()
} else {
targetEl = target;
}
if (!targetEl) return;
const resizeObserver = new ResizeObserver((entries) => {
entries.forEach((entry) => {
const { clientWidth, clientHeight } = entry.target;
setRafState({
width: clientWidth,
height: clientHeight
})
})
})
setResizeObserver(resizeObserver);
resizeObserver?.observe(targetEl);
}, [])
return state;
}
具体使用
export default function App() {
const ref = useRef(null);
const size = useSize(ref);
return (
<div ref={ref} style={{ border: "1px solid red" }}>
<p>Try to resize the preview window </p>
<p>
width: {size?.width}px, height: {size?.height}px
</p>
</div>
);
};
仅作为自己的学习积累,有什么需要改进的地方请大家多多批评指点😅!