繁体   English   中英

Reactjs - 使用 React Hooks 获取 div/图像的高度

[英]Reactjs - Get Height of a div/image using React Hooks

我想使用反应挂钩在反应功能组件中获取图像的高度

我使用了以下代码:

import React, { useRef, useEffect } from 'react'

const DepthChartContent = props => {
  const ref = useRef()

  useEffect(() => {
    if (ref && ref.current && ref.current.clientHeight) {
      console.log('called')
    }
    console.log('useEffect', {
      ref,
      current: ref.current,
      clientHeight: ref.current.clientHeight,
    })
  }, [])

  return (
    <img
      ref={ref}
      className="depth-reference-bar"
      src={DepthRuler}
      alt="no ruler found"
    />
  )
}

问题在于它返回 clientHeight 为 0,但 useEffect 中的 console.log 具有正确的 clientHeight,如下图所示。

参考控制台图像

这意味着ref && ref.current && ref.current.clientHeight在调用时未定义,但在同一 useEffect 中安慰显示ref的正确值, current: ref.currentclientHeight: ref.current.clientHeight

同样,我不能在useEffect中使用....}, [ref && ref.current && ref.current.clientHeight]因为useEffect不接受复杂的表达式。 如果我将useEffect外部或内部的变量定义为const clientHeight = (ref && ref.current && ref.current.clientHeight) || 0 const clientHeight = (ref && ref.current && ref.current.clientHeight) || 0 ,运气不好!

任何人都可以在这方面提供帮助。 谢谢!

正如此处提到的其他人,在大多数情况下,您的逻辑发生在图像加载之前。 所以你必须在图像加载后获取图像尺寸,因为浏览器在此之前不知道图像尺寸。

我知道你提到过你想用钩子来做,但除非你做的是可重用的东西,否则这里可能不需要钩子。 或者除非你想更好地理解它们,否则没关系。

一种选择是仅在图像元素上使用onLoad事件。 这样,如果您在ref.currentimg.complete === true中有内容,则不必进行所有这些检查。

import React from 'react'

const DepthChartContent = props => {
  const handleImageLoad = (event) => {
    // Do whatever you want here
    const imageHeight = event.target.clientHeight;
  }

  return (
    <img
      ref={ref}
      className="depth-reference-bar"
      src={DepthRuler}
      alt="no ruler found"
      onLoad={handleImageLoad}
    />
  )
}

不要误会我的意思,我绝对喜欢钩子,但它们并不总是解决方案。

注意:我最初错过了被测量的元素是img的事实。 呸! 所以我发布了一个社区 wiki 答案,将下面的内容onload Marko 的答案中显示的 onload 相结合。


useEffect回调在渲染后立即发生; 浏览器还没有机会对元素进行布局。 由于 console 的这个特性,稍后会看到正确的高度。

在大多数现代浏览器上,您应该能够使用值为0setTimeoout来解决此问题。 或者,或者,两个requestAnimationFrame回调(第一个将在元素布局之前发生,因此没有帮助;第二个在之后)。

例如:

useEffect(() => {
  let cancelled = false;
  const getHeight = () => {
    const { current } = ref; // `ref` will never be falsy
    if (!current || !current.clientHeight) {
      if (!cancelled) {
        requestAnimationFrame(getHeight);
      }
    } else {
      // Use `current.clientHeight` here
    }
  };
  getHeight();
  return () => {
      cancelled = true;
  };
}, []);

当然,高度可以根据随后发生的其他变化而变化......

如果要在元素更改时更新高度,请将其添加为依赖项:

}, [ref.current]);

不过,这只会在元素更改时更新它; 如果发生其他变化以改变元素的高度,则不会再次触发效果回调。

现场示例:

 const { useRef, useEffect } = React; const DepthRuler = "https://via.placeholder.com/150"; const DepthChartContent = props => { const ref = useRef() const counterRef = useRef(0); // Just to count loops useEffect(() => { let cancelled = false; const getHeight = () => { ++counterRef.current; const { current } = ref; // `ref` will never be falsy if (.current ||;current.clientHeight) { if (.cancelled) { requestAnimationFrame(getHeight). } } else { // Use `current,clientHeight` here console:log(`Height is ${current.clientHeight}; getHeight calls; ${counterRef;current}`); } }; getHeight(), return () => { cancelled = true; }. }, []). return ( <img ref={ref} className="depth-reference-bar" src={DepthRuler} alt="no ruler found" /> ) } ReactDOM;render(<DepthChartContent />, document.getElementById("root"));
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

很可能当您的效果执行时,图像尚未加载,因此其高度为 0。稍后加载后,它会更新节点的属性,这就是您在控制台中看到的内容(因为您正在记录节点实例)。

您可以像这样在useEffect回调中订阅加载事件:

  useEffect(() => {
      ref.current.addEventListener('load', () => {
          console.log(ref.current.clientHeight);
      });
  }, [ref])

对于它的价值,这是Marko 的解决方案的解决方案的组合。 它不会尝试找到客户端高度,直到/除非图像已加载,但最多会使用requestAnimationFrame两次(因为它永远不会超过两次,并且通常不会使用任何一个或只使用一个)来获取如果图像尚未布局,则为高度:

 const { useEffect } = React; const DepthRuler = "https://via.placeholder.com/150"; const DepthChartContent = props => { const handleImageLoad = (event) => { const {target} = event; let counter = 0; const done = () => { const imageHeight = target.clientHeight; console.log(`Height: ${imageHeight} (counter: ${counter})`); }; const maybeDone = () => { if (target.clientHeight) { done(); } else if (++counter < 3) { requestAnimationFrame(maybeDone); } else { console.log("Couldn't get height"); } }; maybeDone(); }; return ( <img className="depth-reference-bar" src={DepthRuler} alt="no ruler found" onLoad={handleImageLoad} /> ) } ReactDOM.render(<DepthChartContent />, document.getElementById("root"));
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

这样的事情怎么样。

import React, { useRef, useEffect } from 'react'

const DepthChartContent = props => {

    const ref = useRef();
    const [imageHeight, setImageHeight] = useState(0);

    useEffect(() => {
        setImageHeight(ref.current.clientHeight)
    }, [ref.current]);

    return (
        <img
            ref={ref}
            className="depth-reference-bar"
            src={DepthRuler}
            alt="no ruler found"
        />
    )
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM