meowzart
meowzart

Reputation: 85

Removing event listener after useEffect runs causing scrollIntoView() to never go off?

Here is my code

const [isScrollLocked, setIsScrollLocked] = useState(false)

  const handleScroll = (section, scrollLocked) => {
    if (!scrollLocked) {
      document.getElementById(section).scrollIntoView()
      document.querySelector('body').classList.add('overflow-hidden')
      setIsScrollLocked(true)
      setTimeout(() => {
        document.querySelector('body').classList.remove('overflow-hidden')
        setIsScrollLocked(false)
      }, 1500)
    }
  }

  useEffect(() => {
    const handler = (e) => handleScroll(nextSection, isScrollLocked)

    document.addEventListener('scroll', handler)

    // cleanup callback, that will be called before the effect runs again

    return () => document.removeEventListener('scroll', handler)
  }, [nextSection, isScrollLocked])

It seems that the callback cleanup that runs after the useEffect is making it so that the scrollIntoView inside handleScroll never takes place. However if I console.log before or after it fires off. If I remove the callback the scrollIntoView works as normal but I am stacking up thousands of scroll events and crashing my application.

If anybody could spot why the callback cleanup is having this effect and explain it to me I would really like to know.

Upvotes: 0

Views: 493

Answers (1)

Joseph Cho
Joseph Cho

Reputation: 4214

The problem is how your setTimeout and useEffect are interacting. Your useEffect is running everytime isScrollLocked changes. Since your setTimeout is changing isScrollLocked, it makes it look like useEffect is running 1.5 seconds later.

Try removing the isScrollLocked watcher:

  const [isScrollLocked, setIsScrollLocked] = useState(false);

  const handleScroll = (section) => {
    if (!isScrollLocked) {
      document.getElementById(section).scrollIntoView();
      document.querySelector("body").classList.add("overflow-hidden");
      setIsScrollLocked(true);
      setTimeout(() => {
        document.querySelector("body").classList.remove("overflow-hidden");
        setIsScrollLocked(false);
      }, 1500);
    }
  };

  useEffect(() => {
    const handler = (e) => handleScroll(nextSection);
    document.addEventListener("scroll", handler);

    // cleanup callback, that will be called before the effect runs again
    return () => document.removeEventListener("scroll", handler);
  });

Here's a link: https://codesandbox.io/s/react-hooks-counter-demo-forked-7n064

Upvotes: 1

Related Questions