实现精简版useSize钩子函数

836 阅读1分钟

实现精简版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>
  );
};

仅作为自己的学习积累,有什么需要改进的地方请大家多多批评指点😅!