我正在尝试创建一个范围输入,该范围输入在滑块的正上方显示一个工具提示。
我在线上浏览了一些普通的JS示例,看来我需要具有元素的宽度才能完成这一工作。
所以我只是想知道如何获得元素的宽度?
几乎等同于JQuery方法 $(element).width()
我正在尝试创建一个范围输入,该范围输入在滑块的正上方显示一个工具提示。
我在线上浏览了一些普通的JS示例,看来我需要具有元素的宽度才能完成这一工作。
所以我只是想知道如何获得元素的宽度?
几乎等同于JQuery方法 $(element).width()
Answers:
class MyComponent extends Component {
constructor(props){
super(props)
this.myInput = React.createRef()
}
componentDidMount () {
console.log(this.myInput.current.offsetWidth)
}
render () {
return (
// new way - as of React@16.3
<div ref={this.myInput}>some elem</div>
// legacy way
// <div ref={(ref) => this.myInput = ref}>some elem</div>
)
}
}
React.createRef()
)
带挂钩:
const MyComponent = () => {
const ref = useRef(null);
useEffect(() => {
console.log('width', ref.current ? ref.current.offsetWidth : 0);
}, [ref.current]);
return <div ref={ref}>Hello</div>;
};
useRef
和useEffect
你可以简单地在上面的例子中使用回调REF。
const ref = useRef<HTMLHeadingElement>(null);
实际上,最好在自定义hook中隔离此调整大小逻辑。您可以这样创建一个自定义钩子:
const useResize = (myRef) => {
const [width, setWidth] = useState(0)
const [height, setHeight] = useState(0)
useEffect(() => {
const handleResize = () => {
setWidth(myRef.current.offsetWidth)
setHeight(myRef.current.offsetHeight)
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
}
}, [myRef])
return { width, height }
}
然后您可以像这样使用它:
const MyComponent = () => {
const componentRef = useRef()
const { width, height } = useResize(componentRef)
return (
<div ref={myRef}>
<p>width: {width}px</p>
<p>height: {height}px</p>
<div/>
)
}
handleResize()
on EventListeners上,否则useResize将返回{ 0, 0}
useResize
需要包括`if(myRef.current){<setWidth SetHeight等,>)
这基本上是MarcoAntônio对于React自定义钩子的答案,但经过修改后可以设置初始尺寸,而不仅仅是在调整大小之后。
export const useContainerDimensions = myRef => {
const getDimensions = () => ({
width: myRef.current.offsetWidth,
height: myRef.current.offsetHeight
})
const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
useEffect(() => {
const handleResize = () => {
setDimensions(getDimensions())
}
if (myRef.current) {
setDimensions(getDimensions())
}
window.addEventListener("resize", handleResize)
return () => {
window.removeEventListener("resize", handleResize)
}
}, [myRef])
return dimensions;
};
使用方式相同:
const MyComponent = () => {
const componentRef = useRef()
const { width, height } = useContainerDimensions(componentRef)
return (
<div ref={componentRef}>
<p>width: {width}px</p>
<p>height: {height}px</p>
<div/>
)
}
一个简单且最新的解决方案是使用React React useRef钩子,该钩子存储对组件/元素的引用,并结合使用useEffect钩子,该钩子在组件渲染时触发。
import React, {useState, useEffect, useRef} from 'react';
export default App = () => {
const [width, setWidth] = useState(0);
const elementRef = useRef(null);
useEffect(() => {
setWidth(elementRef.current.getBoundingClientRect().width);
}, []); //empty dependency array so it only runs once at render
return (
<div ref={elementRef}>
{width}
</div>
)
}
if (!ref.current) return;
在使用ref之前添加,并将ref添加到依赖项数组,这样看起来就好像[ref]
不是一个空的数组
这是@meseern答案的TypeScript版本,避免了重新渲染时不必要的分配:
import React, { useState, useEffect } from 'react';
export function useContainerDimensions(myRef: React.RefObject<any>) {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useEffect(() => {
const getDimensions = () => ({
width: (myRef && myRef.current.offsetWidth) || 0,
height: (myRef && myRef.current.offsetHeight) || 0,
});
const handleResize = () => {
setDimensions(getDimensions());
};
if (myRef.current) {
setDimensions(getDimensions());
}
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, [myRef]);
return dimensions;
}
一个好的做法是侦听调整大小事件,以防止在渲染时调整大小,甚至防止可能会导致应用程序出错的用户窗口调整大小。
const MyComponent = ()=> {
const myRef = useRef(null)
const [myComponenetWidth, setMyComponentWidth] = useState('')
const handleResize = ()=>{
setMyComponentWidth(myRef.current.offsetWidth)
}
useEffect(() =>{
if(myRef.current)
myRef.current.addEventListener('resize', handleResize)
return ()=> {
myRef.current.removeEventListener('resize', handleResize)
}
}, [myRef])
return (
<div ref={MyRef}>Hello</div>
)
}
这可以通过使用回调refs以更简单的方式处理。
React允许您将函数传递给ref,该ref返回基础DOM元素或组件节点。请参阅:https://reactjs.org/docs/refs-and-the-dom.html#callback-refs
const MyComponent = () => {
const myRef = node => console.log(node ? node.innerText : 'NULL!');
return <div ref={myRef}>Hello World</div>;
}
每当更改基础节点时,就会触发此函数。更新之间将为null,因此我们需要检查一下。例:
const MyComponent = () => {
const [time, setTime] = React.useState(123);
const myRef = node => console.log(node ? node.innerText : 'NULL!');
setTimeout(() => setTime(time+1), 1000);
return <div ref={myRef}>Hello World {time}</div>;
}
/*** Console output:
Hello World 123
NULL!
Hello World 124
NULL!
...etc
***/
尽管这不能像这样处理调整大小(我们仍然需要调整大小的侦听器来处理用户调整窗口的大小),但我不确定这是OP所要求的。并且此版本将处理由于更新而调整节点大小的操作。
因此,这是一个基于此想法的自定义钩子:
export const useClientRect = () => {
const [rect, setRect] = useState({width:0, height:0});
const ref = useCallback(node => {
if (node !== null) {
const { width, height } = node.getBoundingClientRect();
setRect({ width, height });
}
}, []);
return [rect, ref];
};
以上基于https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node
注意,该钩子返回一个ref回调,而不是传递给ref。我们使用useCallback来避免每次都重新创建一个新的ref函数;不是很重要,但被认为是好的做法。
用法如下(基于MarcoAntônio的示例):
const MyComponent = ({children}) => {
const [rect, myRef] = useClientRect();
const { width, height } = rect;
return (
<div ref={myRef}>
<p>width: {width}px</p>
<p>height: {height}px</p>
{children}
<div/>
)
}