Reputation: 123
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
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