简体   繁体   English

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

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

I want to get Height of an image in a react functional component using react hooks.我想使用反应挂钩在反应功能组件中获取图像的高度

I have used below code:我使用了以下代码:

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"
    />
  )
}

The problem with this is that it returns the clientHeight as 0 but console.log in useEffect has the correct clientHeight as shown in below pic.问题在于它返回 clientHeight 为 0,但 useEffect 中的 console.log 具有正确的 clientHeight,如下图所示。

参考控制台图像

This means that ref && ref.current && ref.current.clientHeight is not defined when called but consoling in same useEffect is showing correct value for ref , current: ref.current but clientHeight: ref.current.clientHeight is ZERO.这意味着ref && ref.current && ref.current.clientHeight在调用时未定义,但在同一 useEffect 中安慰显示ref的正确值, current: ref.currentclientHeight: ref.current.clientHeight

Similarly, I can't use ....}, [ref && ref.current && ref.current.clientHeight] in useEffect because useEffect do not accept complex expression.同样,我不能在useEffect中使用....}, [ref && ref.current && ref.current.clientHeight]因为useEffect不接受复杂的表达式。 If I defined a variable outside or inside useEffect as const clientHeight = (ref && ref.current && ref.current.clientHeight) || 0如果我将useEffect外部或内部的变量定义为const clientHeight = (ref && ref.current && ref.current.clientHeight) || 0 const clientHeight = (ref && ref.current && ref.current.clientHeight) || 0 , no luck! const clientHeight = (ref && ref.current && ref.current.clientHeight) || 0 ,运气不好!

Can anyone help in this regards.任何人都可以在这方面提供帮助。 Thanks!谢谢!

As others mentioned here, your logic happens before image is loaded in most cases.正如此处提到的其他人,在大多数情况下,您的逻辑发生在图像加载之前。 So you have to get the image dimensions after the image has loaded because the browser doesn't know the image dimensions before that.所以你必须在图像加载后获取图像尺寸,因为浏览器在此之前不知道图像尺寸。

I know you mentioned you want to do it with hooks, but unless you are doing something reusable, there's probably no need for hooks here.我知道你提到过你想用钩子来做,但除非你做的是可重用的东西,否则这里可能不需要钩子。 Or unless you are trying to understand them better, then it's fine.或者除非你想更好地理解它们,否则没关系。

One option would be to just use the onLoad event on the image element.一种选择是仅在图像元素上使用onLoad事件。 This way you don't have to do all these checks if you have something in ref.current or is img.complete === true .这样,如果您在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}
    />
  )
}

Don't get me wrong, I absolutely love hooks, but they aren't always the solution.不要误会我的意思,我绝对喜欢钩子,但它们并不总是解决方案。

Note: I originally missed the fact the element being measured is an img .注意:我最初错过了被测量的元素是img的事实。 Doh!呸! So I posted a community wiki answer combining the below with the onload shown in Marko's answer .所以我发布了一个社区 wiki 答案,将下面的内容onload Marko 的答案中显示的 onload 相结合。


useEffect callbacks happen immediately after render; useEffect回调在渲染后立即发生; the browser won't have had a chance to layout the elements yet.浏览器还没有机会对元素进行布局。 You're seeing the correct height later becaue of this feature of console .由于 console 的这个特性,稍后会看到正确的高度。

On most modern browsers, you should be able to fix this using a setTimeoout with the value 0 .在大多数现代浏览器上,您应该能够使用值为0setTimeoout来解决此问题。 Or, alternately, two requestAnimationFrame callbacks (the first will happen just before the element is laid out, so not helpful; the second is afterward).或者,或者,两个requestAnimationFrame回调(第一个将在元素布局之前发生,因此没有帮助;第二个在之后)。

For instance:例如:

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;
  };
}, []);

Of course, the height can change based on other things changing subsequently...当然,高度可以根据随后发生的其他变化而变化......

If you want to update the height when the element changes, add it as a dependency:如果要在元素更改时更新高度,请将其添加为依赖项:

}, [ref.current]);

That will only update it when the element changes, though;不过,这只会在元素更改时更新它; if something else changes to change the height of the element, that won't fire the effect callback again.如果发生其他变化以改变元素的高度,则不会再次触发效果回调。

Live Example:现场示例:

 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>

Most likely when your effect executes the image hasn't been loaded yet thus its height is 0. Later when it has been loaded it updates the node's attributes and that's what you see in the console (since you're logging the node instance).很可能当您的效果执行时,图像尚未加载,因此其高度为 0。稍后加载后,它会更新节点的属性,这就是您在控制台中看到的内容(因为您正在记录节点实例)。

You can subscribe to the load event in your useEffect callback like this:您可以像这样在useEffect回调中订阅加载事件:

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

For what it's worth, here's a combination of Marko's solution and mine .对于它的价值,这是Marko 的解决方案的解决方案的组合。 It doesn't try to find the client height until/unless the image has been loaded, but will use requestAnimationFrame at most twice (because it should never take more than two, and frequently won't take any or just one) to get the height if the image hasn't been laid out yet:它不会尝试找到客户端高度,直到/除非图像已加载,但最多会使用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>

How about something like this.这样的事情怎么样。

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