Я создаю веб-сайт с использованием 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
Эта текущая настройка работает хорошо. Я могу наблюдать за детьми ParentComponent
, используя IntersectionObserver
.
Однако мне нужно преобразовать этот компонент в функциональный компонент. Я пытался реализовать эту же логику c, используя 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 , IntersectionObserver
недоступен во время процесса сборки Gatsby, поскольку Gatsby использует рендеринг на стороне сервера.
const ParentComponent = () => {
const observer = useRef(new IntersectionObserver())
// Throws an error: IntersectionObserver is not defined.
}