I'm building a portfolio site with gatsby where I have a sidebar on the left with some links (Home, about, work, etc.) and on the right there's the main content which is one long strip of sections. Clicking a link simply scrolls to that section. I would like for a link to be styled differently when the user reaches that section. I've found a solution in vanilla javascript to apply active state that works, but I don't know how to do this the react way.
useEffect(() => {
const navbarlinks = document.querySelectorAll("nav a");
const navbarlinksActive = () => {
let position = window.scrollY + 200;
navbarlinks.forEach((navbarlink) => {
let section = document.querySelector(navbarlink.hash);
if (position >= section.offsetTop && position <= section.offsetTop + section.offsetHeight) {
navbarlink.classList.add("active");
} else {
navbarlink.classList.remove("active");
}
});
};
window.addEventListener("load", navbarlinksActive);
document.addEventListener("scroll", navbarlinksActive);
return () => {
window.removeEventListener("load", navbarlinksActive);
document.removeEventListener("scroll", navbarlinksActive);
};
}, []);
I'm selecting all the nav links, then looping through them, getting the hash property, which will match the id of the corresponding section which is then selected. Then the class is added or removed based on its position. This code is in the sidebar component where my links, which are gatsby Link elements, also are.
return (
<nav>
<Link
className="hover:text-clr-accent flex items-center py-2 px-4 transition"
to="#home"
>
<i className="fa-solid fa-house w-6 text-center text-lg"></i>
<span className="pl-2 text-lg">Home</span>
</Link>
<Link
className="hover:text-clr-accent flex items-center py-2 px-4 transition"
to="#resume"
>
<i className="fa-solid fa-file w-6 text-center text-lg"></i>
<span className="pl-2 text-lg">Resume</span>
</Link>
<Link
className="hover:text-clr-accent flex items-center py-2 px-4 transition"
to="#about"
>
<i className="fa-solid fa-address-card w-6 text-center text-lg"></i>
<span className="pl-2 text-lg">About me</span>
</Link>
<Link
className="hover:text-clr-accent flex items-center py-2 px-4 transition"
to="#work"
>
<i className="fa-solid fa-briefcase w-6 text-center text-lg"></i>
<span className="pl-2 text-lg">My work</span>
</Link>
</nav>
);
Instead of directly adding/removing the active class set the active link Id to state and render based on that. Otherwise react may overwrite your classes by rendering
Don't use the load event listener instead just call navbarlinksActive(), the window is already loaded by the time your effect is called.