How to set root in react-intersection-observer

1.5k Views Asked by At

I got a list based on I render list items I want to track which elements overflows container (especially index of first elements that overflew) in order to do this I wanted to use react-intersection-observer library.

My problem:

const List = ({data}) => {
 const { ref, inView, entry } = useInView({
  threshold: 0,
  root: ????   
 });

 return (
  <div>{data.map(i=><ListItem />)</div>
 )
}

My solution which does not work.

const wrapperRef = useRef(null);
const { ref, inView, entry } = useInView({
 threshold: 0,
 root: wrapperRef  
});

return (
 <div ref={wrapperRef}>{data.map(i=><div ref={ref}><ListItem /></div>)</div>
)

Failed to construct 'IntersectionObserver': Failed to read the 'root' property from 'IntersectionObserverInit': The provided value is not of type '(Document or Element)'.

My question if my approach is correct and if yes how to set root?

2

There are 2 best solutions below

0
On

The react-intersection-observer docs mention to use the ref it gives you from useInView. No need to create a new one. Also root defaults to the viewport of the entire document which is probably what you want.

const List = ({data}) => {
 const { ref, inView, entry } = useInView();

 return (
  <div ref={ref}>{data.map(i=><ListItem />)</div>
 )
}

If you don't want root to be the document viewport but instead a container, you can give useInView a root ref:

const container = useRef(null)
const ref = useRef(null)
const isInView = useInView({ root: container })

return (
  <div ref={container} style={{ overflow: "scroll" }}>
    <div ref={ref} />
  </div>
)

There's many good examples here as well.

0
On

I hope you've figured it out by now, but for anyone else encountering issues getting this work, the error the original poster mentioned about the type mismatch is a hint in the solution direction. You actually need to pass the value of the ref, not the react ref into the options argument of the useInView hook - modifying the provided example:

const wrapperRef = useRef(null);
const { ref, inView, entry } = useInView({
 threshold: 0,
 root: wrapperRef.current  // This is the missing part from the initial example :)
});

return (
 <div ref={wrapperRef}>{data.map(i=><div ref={ref}><ListItem /></div>)</div>
)

Hope this helps!