remove the side-effect of function when the component is unmounted in react?

0

Issue

I am developing a react-app and have used the following function to track the active links in my navigation panel.

The following function uses IntersectionObserver to track if the given element is in the viewport or not.

The code is able to solve its motive.
But when I change the route to different component it shows a error – TypeError: Cannot read property 'parentElement' of null

That’s because the component using the function changeActiveLink(); is not longer in the page (it was unmounted).

I want to remove the effects of this function when the component is removed.

I tried integrating it with DOMContentLoaded eventListener and then cleaning that event listener but this event listener only works on first render of HTML document and does not works when we render the component on required route leaving the function changeActiveLink(); uninitialized.

How should I make this function work only in this specific component and avoid its effect on other components ?

Screenshot of error screen

useEffect(() => {
    const changeActiveLink = () => {
      
      const scrollHandler = (entries) => {
        entries.forEach((entry) => {
          const id = entry.target.getAttribute("id");

          if (entry.intersectionRatio > 0) {
            document
              .querySelector(`nav li a[href="#${id}"]`)
              .parentElement.classList.add("active");
          } else {
            document
              .querySelector(`nav li a[href="#${id}"]`)
              .parentElement.classList.remove("active");
          }
        });
      };

      const observer = new IntersectionObserver(scrollHandler);

      // Track all sections that have an `id` applied
      document.querySelectorAll("section[id]").forEach((section) => {
        observer.observe(section);
      });
    };

    changeActiveLink();
    
  }, []);

Solution

Return a useEffect cleanup function that will un-observe the target element:

useEffect(() => {
  const changeActiveLink = () => {

    const scrollHandler = (entries) => {
      entries.forEach((entry) => {
        const id = entry.target.getAttribute("id");

        if (entry.intersectionRatio > 0) {
          document
            .querySelector(`nav li a[href="#${id}"]`)
            .parentElement.classList.add("active");
        } else {
          document
            .querySelector(`nav li a[href="#${id}"]`)
            .parentElement.classList.remove("active");
        }
      });
    };

    const observer = new IntersectionObserver(scrollHandler);

    const sections = document.querySelectorAll("section[id]");

    // Track all sections that have an `id` applied
    sections.forEach((section) => {
      observer.observe(section);
    });
    
    return () => {
      // untrack all sections that have an `id` applied
      sections.forEach((section) => {
        observer.unobserve(section);
      });
    }; // return the cleanup function
  };

  return changeActiveLink(); // you should also return the function here

}, []);

Answered By – Ori Drori

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave A Reply

Your email address will not be published.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More