Reputation: 745
The following code will trigger a state change as soon as the element enters or leaves the viewport by scrolling
const dialog = useRef(),
[visible, set] = useState(false)
useEffect(() => {
const observer = new IntersectionObserver((e) => {
set(e[0].isIntersecting ? true : false)
}, {root: null, rootMargin: '0px', threshold: 0})
observer.observe(dialog.current)
return () => observer.disconnect()
}, [dialog.current])
return <div ref={dialog}>my dialog</div>
but if I try something like this
const observer = new IntersectionObserver((e) => {
set(e[0].boundingClientRect.y <= 0 ? true : false)
}, {root: null, rootMargin: '0px', threshold: 0})
the state gets updated only if a re-render is triggered, while I want it to trigger when I scroll the viewport, based on the boundingClientRect.y
value of the observed element.
the only way I found to make this work is by using window.onscroll
event inside useEffect
and putting the observer inside it but it doesn't look too good to me. Is there a different (and better) way than using window.onscroll
?
Upvotes: 0
Views: 5441
Reputation: 26
This is to do with the DOM content not fully loading by the time the IntersectionObserver
is instantiated in the useEffect
hook.
A way around this, without using window.onscroll
, is to run the hook in a component and pass your setter as a prop.
Something like this
function MyObserver({ selector, callback }){
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => callback(entries),
{ root: null, rootMargin: '0px', threshold: 0 });
const element = document.querySelector(selector);
observer.observe(element);
return () => {
observer.disconnect();
};
}, []);
return null;
}
function Main() {
[visible, set] = useState(false);
return <>
<div className="myDialog">my dialog</div>
<MyObserver selector=".myDialog"
callback={ (e) => { set(e[0].isIntersecting ? true : false); } } />
</>;
}
Now the observer logic is part of the render flow, taking place after the dialog, so it should update correctly.
This worked for me when faced with this exact issue.
Upvotes: 1