Skip to content

Instantly share code, notes, and snippets.

@joshuacerbito
Last active January 8, 2024 13:44
Show Gist options
  • Save joshuacerbito/ea318a6a7ca4336e9fadb9ae5bbb87f4 to your computer and use it in GitHub Desktop.
Save joshuacerbito/ea318a6a7ca4336e9fadb9ae5bbb87f4 to your computer and use it in GitHub Desktop.
Custom React hook for listening to scroll events
/**
* useScroll React custom hook
* Usage:
* const { scrollX, scrollY, scrollDirection } = useScroll();
*/
import { useState, useEffect } from "react";
export function useScroll() {
const [lastScrollTop, setLastScrollTop] = useState(0);
const [bodyOffset, setBodyOffset] = useState(
document.body.getBoundingClientRect()
);
const [scrollY, setScrollY] = useState(bodyOffset.top);
const [scrollX, setScrollX] = useState(bodyOffset.left);
const [scrollDirection, setScrollDirection] = useState();
const listener = e => {
setBodyOffset(document.body.getBoundingClientRect());
setScrollY(-bodyOffset.top);
setScrollX(bodyOffset.left);
setScrollDirection(lastScrollTop > -bodyOffset.top ? "down" : "up");
setLastScrollTop(-bodyOffset.top);
};
useEffect(() => {
window.addEventListener("scroll", listener);
return () => {
window.removeEventListener("scroll", listener);
};
});
return {
scrollY,
scrollX,
scrollDirection
};
}
@nhuanhoangduc
Copy link

Only register 'scroll' event one time:

import { useState, useEffect, useCallback } from 'react'

export const useScroll = () => {
  const [state, setState] = useState({
    lastScrollTop: 0,
    bodyOffset: document.body.getBoundingClientRect(),
    scrollY: document.body.getBoundingClientRect().top,
    scrollX: document.body.getBoundingClientRect().left,
    scrollDirection: '', // down, up
  })

  const handleScrollEvent = useCallback((e) => {
    setState((prevState) => {
      const prevLastScrollTop = prevState.lastScrollTop
      const bodyOffset = document.body.getBoundingClientRect()

      return {
        setBodyOffset: bodyOffset,
        scrollY: -bodyOffset.top,
        scrollX: bodyOffset.left,
        scrollDirection: prevLastScrollTop > -bodyOffset.top ? 'down' : 'up',
        lastScrollTop: -bodyOffset.top,
      }
    })
  }, [])

  useEffect(() => {
    const scrollListener = (e) => {
      handleScrollEvent(e)
    }
    window.addEventListener('scroll', scrollListener)

    return () => {
      window.removeEventListener('scroll', scrollListener)
    }
  }, [handleScrollEvent])

  return {
    scrollY: state.scrollY,
    scrollX: state.scrollX,
    scrollDirection: state.scrollDirection,
  }
}

export default useScroll

@Mihai-github
Copy link

Mihai-github commented Oct 13, 2021

Hi,
Really nice what you all did above, my question is the first post made by @joshuacerbito is the updated version after all comments made by the community or anyone came with let's say a "better solution".

Thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment