I'm trying to use the InteractionObserver to show which items are currently in a div viewport,
but when I try to use a state variable inside the callback it always has the initial value, no matter how may times it was updated outside the callback
these are the component's variables
function _IntersectionObserverTest_( props ){
const { data } = props
const scrollDiv = useRef( null )
const itemsRef = useRef( [] )
const [ visible, setVisible ] = useState([])
const visibilityThreshold = 0.5
this is the callback's code
const onInteraction = (entries, opts)=>{
console.log(visible) // <-- never gets updated, it's always []
var onView = [ ...visible ]
entries.forEach( e => {
const key = e.target.getAttribute('itemKey')
const t = e.intersectionRatio
console.log(key, t)
if( t < visibilityThreshold ){
onView = onView.filter( k => k != key )
} else {
onView.push( key )
}
})
setVisible( onView ) // <-- this updates the variable
}
this is the code test
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
<script type="text/babel" defer>
// import React, { useState, useEffect } from "react";
// import ReactDOM from "react-dom";
const { useEffect, useRef, useState, useCallback } = React; // web-browser variant
function IntersectionObserverTest( props ){
const { itemsCount=10 } = props
const data = new Array(itemsCount).fill(null).map( (a, i) => `item ${i}`)
return <_IntersectionObserverTest_ data={data}/>
}
function _IntersectionObserverTest_( props ){
const { data } = props
const scrollDiv = useRef( null )
const itemsRef = useRef( [] )
const [ visible, setVisible ] = useState([])
const visibilityThreshold = 0.5
const getVisible = useCallback(() => {
return visible
}, [ visible ])
useEffect(() => {
itemsRef.current = itemsRef.current.slice(0, props.data.length);
}, [props.data]);
useEffect( () => {
const observer = new IntersectionObserver( onInteraction, {
root: scrollDiv.current,
threshold: visibilityThreshold,
})
for( var i = 0; i < data.length; i++ ){
observer.observe( itemsRef.current[i] )
}
}, [ itemsRef ])
const onInteraction = (entries, opts)=>{
console.log(visible, getVisible())
var onView = [ ...visible ]
entries.forEach( e => {
const key = e.target.getAttribute('itemKey')
const t = e.intersectionRatio
console.log(key, t)
if( t < visibilityThreshold ){
onView = onView.filter( k => k != key )
} else {
onView.push( key )
}
})
setVisible( onView )
}
return <>
<div ref={scrollDiv} style={{width: '100%', overflow: 'scroll'}}>
<div style={{ display: 'flex', flexDirection: 'row' }}>
{ data.map( (d, di) =>
<div
ref={el => itemsRef.current[di] = el}
style={{ display: 'block', minWidth: '200px', width: '200px', border: '1px solid black', padding: '1rem', textAlign: 'center'}}
key={di}
itemKey={`item-${di}`}
>
{d}
</div>
)}
</div>
</div>
<div>
on view:
<div>{visible.map( v => <div key={v}>{v} </div>)}</div>
</div>
</>
}
const rootElement = document.getElementById("root");
ReactDOM.render(<IntersectionObserverTest />, rootElement);
</script>
<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>