React ForwardRef:属性 'current' 在类型 'ForwardedRef<HTMLElement>' 上不存在
React ForwardRef: Property 'current' does not exist on type 'ForwardedRef<HTMLElement>'
我正在尝试创建一个组件来跟踪垂直滚动。问题是——实际的滚动容器不容易预测(在这种特定情况下,它既不是 window
、document
也不是 body
——它是 div#__next
,因为 CSS 溢出规则).
我想保持组件的灵活性和 self-contained。所以我创建了一个 ref
和 DOM 选择器作为参数。我知道这远非惯用语(至少可以说),但令人惊讶的是它似乎有效:
// Parent component
import { useRef } from "react"
const Article = (props) => {
const scrollContainerRef = useRef<HTMLElement | null>(
document.querySelector("#__next") // <-- the scroll container reference
)
return (
<SomeContent>
<ScrollToTop treshold={640} ref={scrollContainerRef} />
</SomeContent>
)
// ScrollToTop
const ScrollToTop = forwardRef(
({ treshold }, ref) => {
const [visible, setVisible] = useState(false)
useEffect(() => {
if (ref?.current) {
ref.current.addEventListener("scroll", throttle(toggleVisible, 300))
return () => {
ref.current.removeEventListener("scroll", throttle(toggleVisible, 300))
}
}
}, [])
// …
所以问题是什么?当前的是 Typescript。我花了几个小时试图让类型正确,但无济于事。 parent 组件是免费的红色波浪线(除非我通过 globalThis
,这似乎至少在 CodeSandbox 中有效),但是每当我访问 current
时 ScrollToTop
都在抱怨属性:
Property 'current' does not exist on type 'ForwardedRef<HTMLElement>'.
我尝试在 parent 和 child 中使用 React.MutableRefObject<HTMLElement | null /* or other T's */>
,但没有用。
知道如何让类型匹配吗?或者这从一开始就是一个愚蠢的想法?
Refs 可能 是具有 .current
属性 的对象,但它们也可能是函数。所以你不能假设转发的 ref 有 .current
属性.
我认为在这里使用 forwardRef
是错误的。 forwardRef
的目的是允许父组件访问子组件中的元素。但是相反,父级是找到元素的那个,然后您将它传递给子级以供其使用。我会为此使用常规状态和道具:
const Article = (props) => {
const [scrollContainer, setScrollContainer] = useState<HTMLElement | null>(() => {
return document.querySelector("#__next");
});
return (
<SomeContent>
<ScrollToTop treshold={640} scrollContainer={scrollContainer} />
</SomeContent>
)
interface ScrollToTopProps {
treshold: number;
scrollContainer: HTMLElement | null;
}
const ScrollToTop = ({ treshold, scrollContainer }: ScrollToTopProps) => {
const [visible, setVisible] = useState(false);
useEffect(() => {
if (scrollContainer) {
const toggle = throttle(toggleVisible, 300);
scrollContainer.addEventListener("scroll", toggle);
return () => {
scrollContainer.removeEventListener("scroll", toggle);
}
}
}, [scrollContainer]);
// ...
}
我正在尝试创建一个组件来跟踪垂直滚动。问题是——实际的滚动容器不容易预测(在这种特定情况下,它既不是 window
、document
也不是 body
——它是 div#__next
,因为 CSS 溢出规则).
我想保持组件的灵活性和 self-contained。所以我创建了一个 ref
和 DOM 选择器作为参数。我知道这远非惯用语(至少可以说),但令人惊讶的是它似乎有效:
// Parent component
import { useRef } from "react"
const Article = (props) => {
const scrollContainerRef = useRef<HTMLElement | null>(
document.querySelector("#__next") // <-- the scroll container reference
)
return (
<SomeContent>
<ScrollToTop treshold={640} ref={scrollContainerRef} />
</SomeContent>
)
// ScrollToTop
const ScrollToTop = forwardRef(
({ treshold }, ref) => {
const [visible, setVisible] = useState(false)
useEffect(() => {
if (ref?.current) {
ref.current.addEventListener("scroll", throttle(toggleVisible, 300))
return () => {
ref.current.removeEventListener("scroll", throttle(toggleVisible, 300))
}
}
}, [])
// …
所以问题是什么?当前的是 Typescript。我花了几个小时试图让类型正确,但无济于事。 parent 组件是免费的红色波浪线(除非我通过 globalThis
,这似乎至少在 CodeSandbox 中有效),但是每当我访问 current
时 ScrollToTop
都在抱怨属性:
Property 'current' does not exist on type 'ForwardedRef<HTMLElement>'.
我尝试在 parent 和 child 中使用 React.MutableRefObject<HTMLElement | null /* or other T's */>
,但没有用。
知道如何让类型匹配吗?或者这从一开始就是一个愚蠢的想法?
Refs 可能 是具有 .current
属性 的对象,但它们也可能是函数。所以你不能假设转发的 ref 有 .current
属性.
我认为在这里使用 forwardRef
是错误的。 forwardRef
的目的是允许父组件访问子组件中的元素。但是相反,父级是找到元素的那个,然后您将它传递给子级以供其使用。我会为此使用常规状态和道具:
const Article = (props) => {
const [scrollContainer, setScrollContainer] = useState<HTMLElement | null>(() => {
return document.querySelector("#__next");
});
return (
<SomeContent>
<ScrollToTop treshold={640} scrollContainer={scrollContainer} />
</SomeContent>
)
interface ScrollToTopProps {
treshold: number;
scrollContainer: HTMLElement | null;
}
const ScrollToTop = ({ treshold, scrollContainer }: ScrollToTopProps) => {
const [visible, setVisible] = useState(false);
useEffect(() => {
if (scrollContainer) {
const toggle = throttle(toggleVisible, 300);
scrollContainer.addEventListener("scroll", toggle);
return () => {
scrollContainer.removeEventListener("scroll", toggle);
}
}
}, [scrollContainer]);
// ...
}