useLayoutEffect,useRef超过了最大更新深度

时间:2020-08-24 11:22:33

标签: reactjs react-hooks

我有以下内容:

// component.js
import React from 'react';
import useComponentSize from 'useComponentSize';

const component = () => {
  const [comSize, comRef] = useComponentSize();

  return (
    <div style={{width: '100%'}} ref={comRef}>
      <p>hi</p>
    </div>
  );
};

正在使用useComponentSize,这是我制作的钩子:

// useComponentSize.js
import {
  useRef,
  useState,
  useLayoutEffect,
} from 'react';

const useComponentSize = () => {
  const [size, setSize] = useState({
    width: 0,
    height: 0,
  });
  const resizeRef = useRef();

  useLayoutEffect(() => {
    setSize(() => ({ 
      width: resizeRef.current.clientWidth,
      height: resizeRef.current.clientHeight,
    }));
  });
  
  return [size, resizeRef];
};

export default useComponentSize;

但是由于某种原因我无法解决,它总是超过最大更新深度。我试过让useLayoutEffect依赖于resizeRef,我认为这会起作用,但是随后它不会再次更新(在反射时,正是我应该期望ref起作用的方式)。 / p>

我应该怎么做才能使其正常工作,最重要的是,为什么上述原因会导致无限循环?


编辑:使用事件侦听器的第二次尝试,仍然失败。我在这里想念什么概念?

// component.js
import React, { useRef } from 'react';
import useComponentSize from 'useComponentSize';

const component = () => {
  const ref = useRef();
  const [comSize] = useComponentSize(ref);

  return (
    <div style={{width: '100%'}} ref={ref}>
      <p>hi</p>
    </div>
  );
};
import {
  useRef,
  useState,
  useLayoutEffect,
} from 'react';

const useComponentSize = (ref) => {
  const [size, setSize] = useState();

  useLayoutEffect(() => {
    const updateSize = () => {
      setSize(ref.current.clientWidth);
    }
    
    ref.current.addEventListener('resize', updateSize);
    updateSize();
    
    return () => window.removeEventListener('resize', updateSize);
  }, []);
  
  return [size];
};

export default useComponentSize;

上面的编辑基于此useWindowSize钩子,该钩子非常有用(我目前正在将其用作替代品,尽管我还是希望上面的方法能起作用,尤其是要知道为什么它不能起作用不起作用)。


对我要达到的目标的简短解释,因为之前没有明确指出:我希望状态size在引用的组件的大小发生变化时进行更新。也就是说,如果窗口调整大小,并且组件保持相同的大小,则不应更新。但是,如果组件大小确实发生变化,则size状态应更改值以反映这一点。

1 个答案:

答案 0 :(得分:1)

由于您尚未将依赖项数组传递给useEffectLayout钩子,因此您的代码陷入了无限循环。

实际上,您根本不需要使用useEffectLayout钩子。您可以使用ResizeObserver API来观察DOM元素的变化。

PS:尽管OP的问题已经通过在问题下方评论之一中发布的演示解决了,但我仍在为将来可能会看到此问题的任何人提供答案。< / p>

示例:

const useComponentSize = (comRef) => {
  const [size, setSize] = React.useState({
    width: 0,
    height: 0
  });

  React.useEffect(() => {
    const sizeObserver = new ResizeObserver((entries, observer) => {
      entries.forEach(({ target }) => {
        setSize({ width: target.clientWidth, height: target.clientHeight });
      });
    });
    sizeObserver.observe(comRef.current);

    return () => sizeObserver.disconnect();
  }, [comRef]);

  return [size];
};

function App() {
  const comRef = React.useRef();
  const [comSize] = useComponentSize(comRef);

  return (
    <div>
      <textarea ref={comRef} placeholder="Change my size"></textarea>
      <h1>{JSON.stringify(comSize)}</h1>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>