dkaoster / scrolly-video

Components for scroll-based (or other externally controlled) playback.
https://scrollyvideo.js.org/
MIT License
971 stars 39 forks source link

Destroy 'scroll' event listener #40

Closed KalebKloppe closed 1 year ago

KalebKloppe commented 1 year ago

Description

In debug mode, I noticed that the 'scroll' event listener was still firing, despite the ScrollyVideo component being removed.

Event Listener Not Destroyed

After some investigation, I found that this was because

  1. the scrollyVideo.destroy() method was never reached in the React component.
  2. the anonymous function wrapping this.updateScrollPercentage in the addEventListener prevented removeEventListener from destroying it.

React Component

(scrollyVideo && scrollyVideo.destroy) always evaluated false and the scrollyVideo.destroy() method couldn't be reached. setScrollyVideo is called inside the useEffect hook, and it may not be set when the cleanup function is called.

const containerElement = useRef(null);
const [scrollyVideo, setScrollyVideo] = useState(null);

useEffect(() => {

  setScrollyVideo(new ScrollyVideo())

  return () => {
    if (scrollyVideo && scrollyVideo.destroy) scrollyVideo.destroy();
  }
}, [containerElement, props]);

I fixed this by using useRef instead of useState to hold the new ScrollyVideo.

const containerElement = useRef(null);
const scrollyVideoRef = useRef(null);

useEffect(() => {

  scrollyVideoRef.current = new ScrollyVideo()

  return () => {
    if (scrollyVideoRef.current && scrollyVideoRef.current.destroy) {
      scrollyVideo.destroy();
  }
}, [containerElement, props]);

Event Listener

Once destroy() could be called, I noticed the event listener was still not being destroyed. After further investigation, I found the anonymous function wrapping this.updateScrollPercentage() prevented the removeEventListener from finding it.

window.addEventListener('scroll', () => this.updateScrollPercentage());
window.removeEventListener('scroll', this.updateScrollPercentage);

was changed to

window.addEventListener('scroll', this.updateScrollPercentage;
window.removeEventListener('scroll', this.updateScrollPercentage);

Other

These changes were tested in React and Vanilla JS but not Svelte, or Vue (as I don't know anything about them).