How to render a component only when they are visible ( react-intersection-observer )

2k views Asked by At

We have some components that we want to render only once they are visible. Once rendered we do not want them to be invisible again.

How can I do this with react-intersection-observer (without creating additional divs)?

3

There are 3 answers

1
Ori Drori On BEST ANSWER

Use the triggerOnce option to avoid tracking the visibility state after the 1st change:

const { ref, inView } = useInView({
  threshold: 0,
  triggerOnce: true
});
0
ic3 On

It's not working an all scenarios so we implemented our own hook using IntersectionObserver :

// should not be the case on modern browsers
const hasIntersectionObserver: boolean = 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window
       && 'isIntersecting' in window.IntersectionObserverEntry.prototype;

/**
/* returns [ref,isVisible]
/* ref is to be used in the element we want to check visibility
/* i.e :  <div ref={ref} />
**/
export function useOnWhenVisible(alwaysVisible: boolean): [(el: Element) => void, boolean] {

    const [ref, setRef] = useState<Element>();
    const [isVisible, setIsVisible] = useState<boolean>(alwaysVisible || !hasIntersectionObserver);

    useEffect(() => {

        // it's just done once
        if (ref == null || isVisible) {
            return undefined;
        }

        const observer = new IntersectionObserver(([entry]) => {
            if (entry.isIntersecting) {
                setIsVisible(true)
            }
        });

        observer.observe(ref);
        return () => {
            observer.disconnect();
        }

    }, [setIsVisible, ref, isVisible]);


    return [setRef, isVisible];
}
0
RinitaPuri On

A few days ago, I encountered a similar problem, but have not yet solved it.
In the library I used (preact-intersection-observer).
there are several options:
rootMargin,
threshold,
defaultInView,
triggerOnce

but the main problem arose when re-rendering the elements.
When the site loaded for the first time, everything worked fine,
but after clicking on the cart icon, or login,
the page was rerendered,
and it seems that the observer did not track further actions, although it writes when the button is clicked that the ref.current is equal to the correct div.
But at the same time, I see a small strip 22px high,
maybe it's the async lazy preact that doesn't load the component.
If we change triggerOnce on false, all works fine, but we also see permanent trigering on off mounted components.
Maybe problem in that part of code:

  // If window is defined, and the observer isn't defined
  if (!!window && !observer.current) {
    observer.current = new IntersectionObserver(
      (entries) => {
        entry.current = entries[0];
        setInView(entries[0].isIntersecting);
      },
      {
        ...options,
        root: ref.current,
      }
    );
  }