React scroll handler hook

Mar 21, 2021

react
gatsby

The scroll event is probably one of the most used events on the webpages. Getting the scroll position of the browser might give you a lot of opportunities to handle user interaction in your web application. In this post, I’d like to show you how I use React hooks to get the current scroll position on the page. I use this solution for example on my personal website.

Basically, all is about using the custom hook which adds an event listener and captures the current scroll position.

useScrollHandler.js

import { useState, useEffect } from "react"
const isWindowAvailable = typeof window !== "undefined"

const getPosition = () => isWindowAvailable ? window.pageYOffset : undefined

const useWindowScrollPosition = () => {

  const [scrollPosition, setScrollPosition] = useState(getPosition())

  useEffect(() => {
    if (!isWindowAvailable) {
      return false
    }

    const handleScroll = () => {
      setScrollPosition(getPosition())
    }

    window.addEventListener("scroll", handleScroll)

    return () => window.removeEventListener("scroll", handleScroll)
  }, [])

  return scrollPosition
}

export default useWindowScrollPosition

You can notice the getPosition function at the top of the file. It is used in the useEffect to check if the window object exists. This is because my website is built using Gatsby. There’s a need to check some globals before calling the code because Gatsby website is built using node. In the Node environment, there are no such objects like window or document, so it will throw an error during the build.

The hook itself is very simple. It returns the current pageYOffset property value. Using hooks like this is very handy because we can reuse them in any component in our code. Now, I use this hook only in the Gatsby layout component, but If I have to implement it elsewhere - I have the scroll handler already. Here’s how the implementation looks like:

layout.js

import React from "react"
import useScrollHandler from "../../hooks/useScrollHandler"
// other imports

const isBrowser = typeof window !== `undefined`
const { arrowThreshold } = config

const Layout = ({ location, children, additionalData }) => {
  return (
    <>
      <Helmet>
        // React Helmet tags
      </Helmet>
      <div className="main-container-mask main-container-mask--top"/>
      <div className="main-container-border"/>
      {
        useScrollHandler() > arrowThreshold &&
          <UpArrow />
      }
      <main className="main-container__content">
        <Navigation location={location} />
        {children}
      </main>
      <div className="main-container-mask main-container-mask--bottom"/>
    </>
  )
}

export default Layout

All I need to do is to check if the return value of the useScroll hook fulfills the condition and the Arrow Up can be rendered. In my case the threshold value is the arrowThresholdand it’s stored in my config file.

That’s all you need to implement the useScrollHandler hook in your application.