在React类组件中使用ResizeObserver

问题描述 投票:0回答:4

我在 Chrome 上使用 React 15,想要连接一个事件监听器来检测父容器的更改。在四处寻找选项后,我遇到了 ResizeObserver,但不知道如何让它在我的项目中工作。

目前,我将其放入构造函数中,但它似乎没有打印任何文本,而且我不确定要在

observe
调用中放入什么内容。

class MyComponent extends React.Component {
    constructor(props) {
        super(props);

        const resizeObserver = new ResizeObserver((entries) => {
            console.log("Hello World");
        });

        resizeObserver.observe(somethingGoesHere);
    }

    render() {
        return (
            <AnotherComponent>
                <YetAnotherComponent>
                </YetAnotherComponent>

                <CanYouBelieveIt>
                </CanYouBelieveIt>

                <RealComponent />
            </AnotherComponent>
        );
    }
}

理想情况下,我也不想将

RealComponent
包装在
div
中并给它
div
一个 id。有办法直接去
RealComponent
吗?

我的目标是观察

RealComponent
的任何调整大小变化,但
MyComponent
也很好。我应该在
somethingGoesHere
槽里放什么?

编辑:

为了让某些东西发挥作用,我硬着头皮在

div
周围包裹了一个
RealComponent
标签。然后我给了它一个 id
<div id="myDivTag">
并更改了
observe
调用:

resizeObserver.observe(document.getElementById("myDivTag"));

但是,当运行这个时,我得到:

未捕获类型错误:resizeObserver.observe 不是函数

任何帮助将不胜感激。

javascript reactjs dom resize
4个回答
19
投票

ComponentDidMount
将是设置观察者的最佳位置,但您也想在
ComponentWillUnmount
上断开连接。

class MyComponent extends React.Component {
  resizeObserver = null;
  resizeElement = createRef();

  componentDidMount() {
    this.resizeObserver = new ResizeObserver((entries) => {
      // do things
    });

    this.resizeObserver.observe(this.resizeElement.current);
  }

  componentWillUnmount() {
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
    }
  }

  render() {
    return (
      <div ref={this.resizeElement}>
        ...
      </div>
    );
  }
}

15
投票

编辑:下面Davidicus的答案更完整,先看那里

ResizeObserver 无法进入构造函数,因为此时 div 在组件生命周期中还不存在。

我认为你无法绕过额外的 div,因为无论如何 React 组件都会减少为 html 元素。

将其放入 componentDidMount 中,它应该可以工作:

componentDidMount() {
   const resizeObserver = new ResizeObserver((entries) => {
        console.log("Hello World");
   });

   resizeObserver.observe(document.getElementById("myDivTag"));
}

7
投票

我最近遇到了类似的问题,不同之处在于我的应用程序主要使用钩子和功能组件。

这是一个如何在 React 功能组件中使用 ResizeObserver 的示例(在打字稿中):

const resizeObserver = React.useRef<ResizeObserver>(new ResizeObserver((entries:ResizeObserverEntry[]) => {
    // your code to handle the size change
}));

const resizedContainerRef = React.useCallback((container: HTMLDivElement) => {
    if (container !== null) {
        resizeObserver.current.observe(container);
    }
    // When element is unmounted, ref callback is called with a null argument
    // => best time to cleanup the observer
    else {
        if (resizeObserver.current)
            resizeObserver.current.disconnect();
    }
}, [resizeObserver.current]);

return <div ref={resizedContainerRef}>
    // Your component content here
</div>;

0
投票
import { useLayoutEffect, useRef, useState } from 'react';
import { shallow } from 'zustand/shallow';

import { debounce } from '@/common/utils';

export interface ObservableElementSize {
  scrollWidth: number;
  offsetWidth: number;
  scrollHeight: number;
  offsetHeight: number;
}

export interface useResizeObserverConfig {
  lazy?: boolean;
  debounceTimeout?: number;
}

export const useResizeObserver = <T extends HTMLElement>(config?: useResizeObserverConfig) => {
  const [size, setSize] = useState<ObservableElementSize | null>();
  const elementRef = useRef<T | null>(null);

  const lazy = config?.lazy ?? false;
  const debounceTimeout = config?.debounceTimeout ?? 200;

  useLayoutEffect(() => {
    if(!elementRef.current) {
      return;
    }

    // initialize value
    setSize((prevState) => {
      const nextState = {
        scrollWidth: elementRef.current!.scrollWidth,
        offsetWidth: elementRef.current!.offsetWidth,
        scrollHeight: elementRef.current!.scrollHeight,
        offsetHeight: elementRef.current!.offsetHeight,
      };

      if(shallow(prevState, nextState)) {
        return prevState;
      }
      return nextState;
    });

    if(lazy) {
      return;
    }

    const debouncedSetSize = debounce(setSize, debounceTimeout);

    const handleSaveNodeSize = (node?: T | null) => {
      if(!node) {
        return;
      }
      debouncedSetSize((prevState) => {
        const nextState = {
          scrollWidth: node.scrollWidth,
          offsetWidth: node.offsetWidth,
          scrollHeight: node.scrollHeight,
          offsetHeight: node.offsetHeight,
        };
        if(shallow(prevState, nextState)) {
          return prevState;
        }
        return nextState;
      });

    };

    const onResize = (entries: ResizeObserverEntry[]) => {
      const [entry] = entries;
      handleSaveNodeSize(entry?.target as T);
    };


    const resizeObserverRef = new ResizeObserver(onResize);


    resizeObserverRef.observe(elementRef.current);


    return () => {
      resizeObserverRef.disconnect();
      debouncedSetSize.cancel();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [elementRef.current, debounceTimeout, lazy]);

  return {
    elementRef,
    size,
  };
};




/**
 * Performs equality by iterating through keys on an object and returning false
 * when any key has values which are not strictly equal between the arguments.
 * Returns true when the values of all keys are strictly equal.
 */
function shallowEqual(objA: mixed, objB: mixed): boolean {
  if (is(objA, objB)) {
    return true;
  }

  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    if (
      !hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {
      return false;
    }
  }

  return true;
}
© www.soinside.com 2019 - 2024. All rights reserved.