ahooks是阿里开源的一套 React Hooks 库,里面封装了大量好用的 Hooks,最近也在项目中频繁使用到了库中useSize这个钩子函数,于是就学习了一下它的源码实现,并且实现了一个精简版useSize。
要点一:ResizeObserver监听 Element 内容区域的边界框改变
要点二:requestAnimationFrame优化高频刷新情况下的数据更新
代码实现
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 67 68 69 70 71 72 73 74 75 76
| 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; }
|
具体使用
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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> ); }
|
仅作为自己的学习积累,有什么需要改进的地方请大家多多批评指点 😅!