Home > other >  getBoundingClientRect() on two React components and check if they overlap onScroll
getBoundingClientRect() on two React components and check if they overlap onScroll

Time:04-08

I want to get a ref, more specifically a getBoundingClientRect() on the <Header/> and <Testimonials/> component. I then want to watch for a scroll event and check if the two components ever overlap. Currently, my overlap variable never flips to true even if what appears on the page is that the two components are overlaping.

  const [isIntersecting, setIsIntersecting] = useState(false)
 
  const header = useRef(null)
  const testimonials = useRef(null)


  const scrollHandler = _ => {
    let headerRect = header.current.getBoundingClientRect();
    let testiRect = testimonials.current.getBoundingClientRect();

    let overlap = !(headerRect.right < testiRect.left ||
      headerRect.left > testiRect.right ||
      headerRect.bottom < testiRect.top ||
      headerRect.top > testiRect.bottom)

    console.log(overlap) // never flips to true
  };

  useEffect(() => {
    window.addEventListener("scroll", scrollHandler, true);
    return () => {
      window.removeEventListener("scroll", scrollHandler, true);
    };
  }, []);

const App = () => {

  return (
    <div className="App">
      <Header />
      <LandingPage />
      <div style={{ height: '100vh', backgroundColor: 'black', color: 'white' }}>
      </div>
      <AboutPage />
      <TestimonialsPage />
      <Footer />
    </div>
  );
}

CodePudding user response:

You'll need to define each of your components as Forwarding Refs, eg

const Header = forwardRef<HTMLElement>((_, ref) => (
  <header ref={ref}>
    <h1>I am the header</h1>
  </header>
));

You can then pass a HTMLElement ref to your components to refer to later

const headerRef = useRef<HTMLElement>(null);

const scrollHandler = () => {
  console.log("header position", headerRef.current?.getBoundingClientRect());
};

useEffect(() => {
  window.addEventListener("scroll", scrollHandler);
  return () => {
    window.removeEventListener("scroll", scrollHandler);
  };
}, []);

return (
  <Header ref={headerRef} />
);

I'm using TypeScript examples since it's easier to translate back down to JS than it is to go up to TS

CodePudding user response:

  • First: Components can't receive directly a ref prop, unless you are wrapping the Component itself in a React.forwardRef wrapper:

     const Component = React.forwardRef((props, ref) => (
        <button ref={ref}>
          {props.children}
        </button>
      ));
    
      // Inside your Parent Component:
    
      const ref = useRef();
      <Component ref={ref}>Click me!</Component>;
    
  • Second: you can also pass a ref down to a child as a standard prop, but you can't call that prop ref since that's a special reserved word just like the key prop:

    const Component= (props) => (
           <button ref={props.myRef}>
             {props.children}
           </button>
         );
    
         // Inside your Parent Component
    
         const ref = useRef();
         <Component myRef={ref}>Click me!</Component>;
    
  • This works perfectly fine, and if it's a your personal project you might work like this with no issues, the only downside is that you have to use custom prop name for those refs, so the code gets harder to read and to mantain, especially if it's a shared repo.

  • Third: Now that you learnt how to gain access to the DOM node of a child Component from its parent, you must know that even if usually it's safe to perform manipulations on those nodes inside a useEffect ( or a componentDidMount ) since they are executed once the DOM has rendered, to be 100% sure you will have access to the right DOM node it's always better using a callback as a ref like this:

     const handleRef = (node) => {
     if (node) //do something with node
     };
     <Component ref={handleRef}/>
    
  • Basically your function hanldeRef will be called by React during DOM node render by passing the node itself as its first parameter, this way you can perform a safe check on the node, and be sure it's 100% valorized when you are going to perform your DOM manipulation.

  • Concerning your specific question about how to access the getBoundingClientRect of a child Component DOM node, I made a working example with both the approaches: https://stackblitz.com/edit/react-pqujuz

  • Related