【发布时间】:2023-03-10 15:50:01
【问题描述】:
Example on CodeSandbox
我正在使用 Gatsby 创建网站,但在将使用 IntersectionObserver 的类组件转换为使用钩子的功能组件时遇到问题。我特别在寻找一种尽可能减少重新渲染次数的解决方案。
我的类组件如下图所示:
父组件使用React.Component
import React, { Component } from "react"
import ChildComponent from "../components/child"
class ParentComponent extends Component {
observer = null
componentDidMount() {
this.observer = new IntersectionObserver(this.handleObserverEvent, {})
}
componentWillUnmount() {
this.observer.disconnect()
this.observer = null
}
handleObserverEvent = entries => {
entries.forEach(entry => console.log(entry))
}
observeElement = ref => {
if (this.observer) this.observer.observe(ref)
}
render() {
const colors = ["red", "orange", "yellow", "green", "blue"]
return (
<div>
{colors.map(color => (
<ChildComponent
key={color}
color={color}
observeElement={this.observeElement}
/>
))}
</div>
)
}
}
export default ParentComponent
子组件
import React, { useRef, useEffect } from "react"
const ChildComponent = ({ color, observeElement }) => {
const ref = useRef()
useEffect(() => {
if (ref.current) observeElement(ref.current)
}, [observeElement])
return (
<div
className={color}
ref={ref}
style={{
width: "100vw",
height: "100vh",
background: color
}}
>
{color}
</div>
)
}
export default ChildComponent
当前的设置运行良好。我可以使用IntersectionObserver 观察ParentComponent 的孩子。
但是,我需要将此组件转换为功能组件。我尝试使用useRef 实现相同的逻辑,但无法获得相同的结果。在observeElement 中,observer.current 等于null。我的功能组件如下所示。
父组件使用useRef
// Omitted imports/exports
const handleObserverEvent = entries => {
entries.forEach(entry => console.log(entry))
}
const ParentComponent = () => {
const observer = useRef(null)
useEffect(() => {
observer.current = new IntersectionObserver(handleObserverEvent, {})
return () => {
if (observer.current) observer.current.disconnect()
observer.current = null
}
}, [])
const observeElement = ref => {
console.log(observer.current) // Logs null
if (observer.current) observer.current.observe(ref)
}
const colors = ["red", "orange", "yellow", "green", "blue"]
return (
<div>
{colors.map(color => (
<ChildComponent
key={color}
color={color}
observeElement={observeElement}
/>
))}
</div>
)
}
为了解决这个问题,我选择使用 useState 来触发重新渲染,这很有效。但是,我想知道是否可以使用useRef 以及可能的其他一些钩子来实现相同的目标。
父组件使用useState
// Omitted imports/exports
const handleObserverEvent = entries => {
// This successfully logs the entries
entries.forEach(entry => console.log(entry))
}
const ParentComponent = () => {
const [observer, setObserver] = useState(null)
useEffect(() => {
setObserver(new IntersectionObserver(handleObserverEvent, {}))
return () => {
if (observer) observer.disconnect()
}
}, [])
const observeElement = ref => {
if (observer) observer.observe(ref)
}
const colors = ["red", "orange", "yellow", "green", "blue"]
return (
<div>
{colors.map(color => (
<ChildComponent
key={color}
color={color}
observeElement={observeElement}
/>
))}
</div>
)
}
注意事项
- 我无法将
new IntersectionObserver()设置为useRef中的初始值。这是因为它在运行gatsby build时会抛出错误。根据这个Stack Overflow answer,IntersectionObserver在 Gatsby 的构建过程中不可用,因为 Gatsby 使用服务器端渲染。
const ParentComponent = () => {
const observer = useRef(new IntersectionObserver())
// Throws an error: IntersectionObserver is not defined.
}
【问题讨论】:
标签: reactjs react-hooks gatsby