我正在尝试创建一个范围输入框,它可以在滑块拇指右上方显示工具提示。
我查阅了一些在线的纯JavaScript示例,似乎需要获取元素的宽度才能实现这个功能。
因此,我想知道如何获取元素的宽度?
基本上等同于jQuery方法$(element).width()
我正在尝试创建一个范围输入框,它可以在滑块拇指右上方显示工具提示。
我查阅了一些在线的纯JavaScript示例,似乎需要获取元素的宽度才能实现这个功能。
因此,我想知道如何获取元素的宽度?
基本上等同于jQuery方法$(element).width()
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()
在构造函数中创建引用) - aaaidan使用hooks:
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
。 - Izhakiconst ref = useRef<HTMLHeadingElement>(null);
。 - Albert TjornejojuseLayoutEffect
,并且要么完全跳过 , [ref.current]
(以便每次渲染都更新它),要么添加一个 ResizeObserver
或其他东西来在调整大小时更新。 - Svish这基本上是Marco Antônio在React自定义钩子方面的答案,但进行了修改以在调整大小之前设置尺寸。
export const useContainerDimensions = myRef => {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
useEffect(() => {
const getDimensions = () => ({
width: myRef.current.offsetWidth,
height: myRef.current.offsetHeight
})
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/>
)
}
实际上,将此调整大小逻辑隔离在一个自定义钩子中会更好。您可以像这样创建一个自定义钩子:
实际上,最好将此调整大小逻辑隔离到自定义钩子中。 您可以像这样创建自定义钩子:
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/>
)
}
handleResize()
on the EventListeners or else useResize will return { 0, 0}
- kcNekouseResize
需要包括 if (myRef.current) {<setWidth SetHeight 等>)
。 - meesernaddEventListener('load', ..)
可以适用于初始大小。 - Shautieh一种简单而现代的解决方案是使用React的 useRef hook 来存储组件/元素的引用,再配合一个useEffect hook,在组件 Render 时触发。
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]
而不是空数组。 - charri这里是一个TypeScript版本的@meseern的答案,它避免了不必要的重新渲染分配:
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 18.x将于2023年面世
出于好的原因,React 18改变了useEffect
的工作方式。对于一个组件来说只运行一次初始化代码是有效的,但在使用useEffect
之前,请先阅读You might not need an effect。要获取元素的尺寸,我们可以使用新的useSyncExternalStore
钩子-
// useDimensions.js
import { useMemo, useSyncExternalStore } from "react"
function subscribe(callback) {
window.addEventListener("resize", callback)
return () => {
window.removeEventListener("resize", callback)
}
}
function useDimensions(ref) {
const dimensions = useSyncExternalStore(
subscribe,
() => JSON.stringify({
width: ref.current?.offsetWidth ?? 0, // 0 is default width
height: ref.current?.offsetHeight ?? 0, // 0 is default height
})
)
return useMemo(() => JSON.parse(dimensions), [dimensions])
}
export { useDimensions }
function MyComponent() {
const ref = useRef(null)
const {width, height} = useDimensions(ref)
return <div ref={ref}>
The dimensions of this div is {width} x {height}
</div>
}
为什么要使用JSON.stringify?
useSyncExternalStore
函数期望 getSnapshot
函数返回一个缓存的值,否则会导致无限次重新渲染。
{width: 300, height: 200} === {width: 300, height: 200}
// => false ❌
JSON.stringify
将对象转换为字符串,以便建立相等关系 -
'{"width":300,"height":200}' === '{"width":300,"height":200}'
// => true ✅
最后,useMemo
钩子确保在后续渲染中返回相同的尺寸对象。当dimensions
字符串发生变化时,记忆会更新,并且使用useDimensions
的组件将重新渲染。
立即可用的尺寸
这里的其他答案要求用户触发resize
事件才能访问尺寸。有些人尝试使用useEffect
内部的手动调用来减轻此问题,但是这些解决方案在React 18中失败了。对于使用useSyncExternalState
的此解决方案不是这种情况。享受在第一次渲染时立即访问尺寸的便利!
typescript
这是适用于typescript用户的useDimensions
hook-
import { RefObject, useMemo, useSyncExternalStore } from "react"
function subscribe(callback: (e: Event) => void) {
window.addEventListener("resize", callback)
return () => {
window.removeEventListener("resize", callback)
}
}
function useDimensions(ref: RefObject<HTMLElement>) {
const dimensions = useSyncExternalStore(
subscribe,
() => JSON.stringify({
width: ref.current?.offsetWidth ?? 0,
height: ref.current?.offsetHeight ?? 0,
})
)
return useMemo(() => JSON.parse(dimensions), [dimensions])
}
export { useDimensions }
如果您只需要获取React元素的宽度,则可以使用此解决方案:
扩充Christopher的评论: 您可以使用'react-use'库来实现此功能,该库还可侦听浏览器的调整大小。有关参考信息: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;
useMeasure
,请访问 https://github.com/pmndrs/react-use-measure。 - GG.有一个库,use-resize-observer,它为您提供了一个基于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>
);
};
React Hook:
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}
当你想要使用时。