Siam Parvez
Siam Parvez

Reputation: 123

Why am I getting error from another component?

I have created an next js app which has two different navbars. One navbar will be used for home and the other will be used for other routes. So in my header.js file I have this set of code -

import React, { useEffect, useState } from "react";
import { useRouter } from "next/router";
import HomeNavbar from "./Navbar/HomeNavbar";
import Navbar from "./Navbar/Navbar";

function Header() {
  const router = useRouter();
  const [isHome, setIsHome] = useState(false);

  useEffect(() => {
    if (router.pathname === "/") {
      setIsHome(true);
    } else {
      setIsHome(false);
    }
  }, [router.pathname]);

  return <>{isHome ? <HomeNavbar /> : <Navbar />}</>;
}

export default Header;

So when I am in home route HomeNavBar is being used and in the HomeNavBar component I have a set of code like this -

useEffect(() => {
    window.onscroll = function() {
      const maxHeight = 192;
      const minHeight = 40;
      let pixel = window.scrollY;
      let pixelPosition =  maxHeight - pixel;
      pixelPosition = pixelPosition > maxHeight ? maxHeight : pixelPosition;
      pixelPosition = pixelPosition < minHeight ? minHeight : pixelPosition;
      document.getElementById("brandmark").style.cssText = `
        height: ${pixelPosition}px; 
        width: auto;
      `;
    }
  }, [])

It's just a basic function where I am doing something with a element containing id="brandmark" But when I am changing the route to /about the function is still running and I am getting an error because that element with that id is not available on the about page or any other route. How can I fix this?

Here is the error message - Unhandled Runtime Error TypeError: Cannot read properties of null (reading 'style')

components\UI\Navbar\HomeNavbar.js (40:43) @ window.onscroll

  38 | pixelPosition = pixelPosition > maxHeight ? maxHeight : pixelPosition;
  39 | pixelPosition = pixelPosition < minHeight ? minHeight : pixelPosition;
> 40 | document.getElementById("brandmark").style.cssText = `
     |                                     ^
  41 |   height: ${pixelPosition}px; 
  42 |   width: auto;
  43 | `;

Upvotes: 2

Views: 87

Answers (1)

Drew Reese
Drew Reese

Reputation: 202686

There is no cleanup function removing the onscroll listener when the component unmounts. This is why you continue to see the event callback called.

It's also considered anti-pattern to set the global onscroll callback as this will overwrite any existing value. It is better to use the addEventListener and removeEventListener functions.

Add a cleanup function to remove the event listener.

useEffect(() => {
  const scrollHandler = function() {
    const maxHeight = 192;
    const minHeight = 40;
    let pixel = window.scrollY;
    let pixelPosition =  maxHeight - pixel;
    pixelPosition = pixelPosition > maxHeight ? maxHeight : pixelPosition;
    pixelPosition = pixelPosition < minHeight ? minHeight : pixelPosition;
    document.getElementById("brandmark").style.cssText = `
      height: ${pixelPosition}px; 
      width: auto;
    `;
  }

  window.addEventListener("scroll", scrollHandler, { passive: true });
  return () => {
    window.removeEventListener("scroll", scrollHandler, { passive: true });
  };
}, []);

If the element with id "brandmark" is sometimes unavailable you should use a null-check/guard-clause to protect against accidental undefined/null property accesses. Use the Optional Chaining operator here.

document.getElementById("brandmark")?.style.cssText = `
  height: ${pixelPosition}px; 
  width: auto;
`;

or using a null-check/guard-clause

document.getElementById("brandmark") && 
document.getElementById("brandmark").style.cssText = `
  height: ${pixelPosition}px; 
  width: auto;
`;

Upvotes: 3

Related Questions