如何得到反应元素的宽度

我试图创建一个范围输入,显示工具提示右上方的滑块拇指。

我在线浏览了一些普通的 JS 示例,似乎我需要元素的宽度来实现这一点。

所以我想知道如何得到元素的宽度?

几乎等同于 JQuery 方法 $(element).width()

228336 次浏览
    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>
)
}
}

钩子:

const MyComponent = () => {
const ref = useRef(null);
useEffect(() => {
console.log('width', ref.current ? ref.current.offsetWidth : 0);
}, [ref.current]);
return <div ref={ref}>Hello</div>;
};

一个好的做法是监听调整大小事件,以防止在呈现时调整大小,甚至防止用户调整窗口大小,这可能会对应用程序造成错误。

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

实际上,最好在 定制钩子中隔离这种调整逻辑。您可以创建一个自定义钩子,如下所示:

const useResize = (myRef) => {
const [width, setWidth] = useState(0)
const [height, setHeight] = useState(0)
  

const handleResize = useCallback(() => {
setWidth(myRef.current.offsetWidth)
setHeight(myRef.current.offsetHeight)
}, [myRef])


useEffect(() => {
window.addEventListener('load', handleResize)
window.addEventListener('resize', handleResize)


return () => {
window.removeEventListener('load', handleResize)
window.removeEventListener('resize', handleResize)
}
}, [myRef, handleResize])


return { width, height }
}

然后你可以像这样使用它:

const MyComponent = () => {
const componentRef = useRef()
const { width, height } = useResize(componentRef)


return (
<div ref={componentRef }>
<p>width: {width}px</p>
<p>height: {height}px</p>
<div/>
)
}

这基本上是马可安东尼奥的答案反应自定义钩,但修改设置的尺寸最初,而不仅仅是调整后的大小。

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

下面是 @ meesern 的回答的 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;
}

一个简单和最新的解决方案是使用 React React UseRef挂钩,它存储对组件/元素的引用,并结合使用 使用效果挂钩,该挂钩在组件呈现时触发。

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

这可以通过使用 复试裁判以一种更简单的方式来处理。

React 允许您将函数传递给 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>;
}

只要基础节点发生更改,就会触发此函数。在更新之间它将为空,因此我们需要检查这一点。例如:

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。我们使用 使用回拨来避免每次重新创建一个新的 ref 函数; 这并不重要,但是可以认为是很好的实践。

用法如下(基于马可 · 安东尼奥的例子) :

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

反应钩:

import React, { useState, useEffect,useRef } from 'react';
...
const table1ref = useRef(null);
const [table1Size, table1SizeSet] = useState({
width: undefined,
height: undefined,
});


useEffect(() => {
function handleResize() {
table1SizeSet({
width: table1ref.current.offsetWidth,
height: table1ref.current.offsetHeight,
});
}
window.addEventListener("resize", handleResize);
handleResize();
return () => window.removeEventListener("resize", handleResize);
}, [ ]);
...
<div  ref={table1ref}>

打电话给:

{table1Size.width}

当你想用的时候。

如果您需要的只是这个问题的标题: 一个反应元素的宽度,那么可以使用这个解决方案。

对克里斯托弗评论的补充: 您可以使用“反应-使用”库来完成此操作。它还会在浏览器调整大小时进行监听。参考文献: < a href = “ https://github.com/stream ich/response-use/blob/master/docs/use測 ure.md”rel = “ nofollow norefrer”> https://github.com/streamich/react-use/blob/master/docs/usemeasure.md

import React from 'react';


import { useMeasure } from 'react-use'; // or just 'react-use-measure'


const sampleView = () => {
const [ref, { width }] = useMeasure<HTMLDivElement>();
console.log('Current width of element', width);
return <div ref={ref}></div>;
};


export default sampleView;

定制钩子

import { useCallback, useState } from "react";


const useContainerDimensions = () => {
const [size, setRect] = useState(null);
const ref = useCallback(node => {
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
return [size, ref];
}
export default useContainerDimensions;

然后你就可以像

const [size, ref] = useContainerDimensions();


return (
<Text ellipsis style=\{\{maxWidth: size?.width}}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore
</Text>
)

有一个库,使用-调整大小-观察者,提供了一个围绕 ResizeObserver构建的钩子。

import React from "react";
import useResizeObserver from "use-resize-observer";


const App = () => {
const { ref, width, height } = useResizeObserver<HTMLDivElement>();


return (
<div>
<div className="instructions">Try resizing this div!</div>
<div ref={ref} className="box">
{width}x{height}
</div>
</div>
);
};

这里有一个反应的例子 为了得到 在页面渲染时以及调整页面大小时元素的宽度

import { useLayoutEffect, useEffect, useRef, useState } from 'react'
const MyComponent = ()=> {
const ref = useRef(null)
const [divWidth, setDivWidth] = useState('')
const handleResize = () => {
setDivWidth(ref.current.offsetWidth)
}
useEffect(() => {
if (ref.current) window.addEventListener('resize',
handleResize)
        

return () => {
window.removeEventListener('resize', handleResize)
}
}, [ref])
        

useLayoutEffect(() => {
setDivWidth(ref.current.offsetWidth)
}, [])
        

return (
<div ref={ref} className="warper">
<div style=\{\{ minWidth: divWidth }}>
hello
</div>
</div>
)
}