[英]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.current
但clientHeight: 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.current
或img.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 的这个特性,你稍后会看到正确的高度。
在大多数现代浏览器上,您应该能够使用值为0
的setTimeoout
来解决此问题。 或者,或者,两个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.