skip to Main Content

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.

What is the best way to do this with react-intersection-observer (without creating additional divs) ?

3

Answers


  1. Chosen as BEST ANSWER

    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];
    }
    

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

    const { ref, inView } = useInView({
      threshold: 0,
      triggerOnce: true
    });
    
    Login or Signup to reply.
  3. 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,
          }
        );
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search