Problem with useEffect and navbar active on scroll "To fix, cancel all subscriptions"

533 views Asked by At

I'm trying to make active anchors in navbar navigation on scroll. Everything is working until I don't change page and return back to home page, then when I scroll page I get an error from useEffect hook " Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. " How I should cancel all subscriptions ?

useEffect code :

const [headerText, setHeader] = useState(false);
let mount = false;    
useEffect(() => {
  if (!mount) {
    scrollActiveNav();
    scrollStickyNav((header) => {
      setHeader(header);
    });
  }
  return () => {
    mount = true;
  };
}, []);

Sticky navbar function :

const scrollStickyNav = (cb) => {
  const scrollSticky = window.addEventListener("scroll", () => {
    const header = document.getElementById("navbar");    
    if (window.pageYOffset >= 80) {
      header.classList.add("navbar-sticky");
      header.classList.remove("absolute");
      cb(true);
    } else {
      header.classList.remove("navbar-sticky");
      header.classList.add("absolute");
      cb(false);
    }
  });
  return window.removeEventListener("scroll", scrollSticky);
}      

Acitve link anchor in navabar function:

const scrollActiveNav = () => {
  const activeNav = window.addEventListener('DOMContentLoaded', () => {
    const options = {
      threshold: 0.5
    };
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        const id = entry.target.id;
        if (entry.isIntersecting && entry.intersectionRatio > 0.5) {
          document.querySelector(`.navbar-nav li a[href="${id}"]`).classList.add('active');
        } else {
          document.querySelector(`.navbar-nav li a[href="${id}"]`).classList.remove('active');
        }
      });
    }, options);
    document.querySelectorAll('section[id]').forEach((section) => {
      observer.observe(section);
    });
  });
  return window.removeEventListener("DOMContentLoaded", activeNav);
}      
2

There are 2 answers

3
lissettdm On

Try change this line let mount = false; for this const mount = useRef(false).

const [headerText, setHeader] = useState(false);
let mount = useRef(false); 
   
useEffect(() => {
  if (!mount.current) {
    scrollActiveNav();
    scrollStickyNav((header) => {
      setHeader(header);
    });
    mount.current = true;
  }
}, []);
0
lissettdm On

Did you try to do something like this?

useEffect(() => {
 scrollActiveNav();
 const activeNav = window.addEventListener('DOMContentLoaded', () => {
    const options = {
      threshold: 0.5
    };
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        const id = entry.target.id;
        if (entry.isIntersecting && entry.intersectionRatio > 0.5) {
          document.querySelector(`.navbar-nav li a[href="${id}"]`).classList.add('active');
        } else {
          document.querySelector(`.navbar-nav li a[href="${id}"]`).classList.remove('active');
        }
      });
    }, options);
    document.querySelectorAll('section[id]').forEach((section) => {
      observer.observe(section);
    });
  });

  return () => {
    window.removeEventListener("DOMContentLoaded", activeNav);
  };
}, []);