1

ReactJS application with a sequence of datacards.

Each datacard has a video that plays in it as well as some other text data.

There are a few hundred of these. Performance is slow... The videos don't play smooth, CPU use skyrockets, the network use goes crazy because we are now downloading 300 videos simultaneously, etc. ... but only about 12 or so are on the screen at any given point in time.

I would like to only load and play the video when the dataccard is on the screen. (or, ideally, when it's 'near' the screen)

So, ever-so-wonderful stackoverflow provided me with this hook: React - check if element is visible in DOM

It is supposed to check if the element is visible .. and then let me react based on that. (ie: I can drop the video, etc)

Unfortunately, when I implement the code as written in that answer:

 export function useOnScreen(ref) {

    const [isIntersecting, setIntersecting] = useState(false)
  
    const observer = new IntersectionObserver(
      ([entry]) => setIntersecting(entry.isIntersecting)
    )
  
    useEffect(() => {
      observer.observe(ref.current)
      // Remove the observer as soon as the component is unmounted
      return () => { observer.disconnect() }
    }, [])
  
    return isIntersecting
  }

and implement the hook in this way:

const ref = useRef()
const isVisible = useOnScreen(ref);
....
return <div ref={ref}>
        {isVisible ? "I'm on the screen" : "I'm not"}
  // actual content of datacard here
</div>

... performance slows to an absolute crawl. I suspect that the process of checking if a datacard is on the screen is taxing.

In the referenced answer, I see someone suggest memo-izing this hook. I'm unsure how to do that and came here for advice.

Alternately, if there is a better way to tell if a react component is on (or near) the screen, that would be great.

lowcrawler
  • 6,777
  • 9
  • 37
  • 79
  • It's difficult to provide help with this without a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). Many factors affect performance in a sizeable way. – jsejcksn Oct 12 '21 at 04:34

2 Answers2

2

Based on the information you've provided, the current version of your hook will create a new IntersectionObserver every time its parent component rerenders. By moving the instantiation of the observer into the effect hook, this duplication is prevented (see below). Beyond that, the information provided in your question is incomplete, so without a Minimal, Reproducible Example, I can only hypothesize about additional causes of performance issues.

import {MutableRefObject, useEffect, useState} from 'react';

export function useOnScreen <T extends HTMLElement>(ref: MutableRefObject<T | null>): boolean {
  const [isIntersecting, setIntersecting] = useState(false);
  const {current: element} = ref;

  useEffect(() => {
    if (!element) return;
    const observer = new IntersectionObserver(([entry]) => setIntersecting(entry.isIntersecting));
    observer.observe(element);
    return () => observer.disconnect();
  }, [element]);

  return isIntersecting;
}
jsejcksn
  • 27,667
  • 4
  • 38
  • 62
0

you can try React.memo https://reactjs.org/docs/react-api.html#reactmemo

this is used when if your component renders the same result given the same props

Shyam
  • 1,364
  • 7
  • 13