Active Links on click navbar
Answered
Ojos Azules posted this in #help-forum
Ojos AzulesOP
Hello, I use window.location.hash so that when a user clicks on a link, it becomes active and changes color. However, sometimes window.location.hash doesn't update as expected. For example, when I click on "Team", it changes, but when I click on "About", it doesn't change.
My code:
My code:
"use client";
import { useEffect, useState } from "react";
import { motion } from 'framer-motion';
import Link from "next/link";
import Image from "next/image";
import Logo from "@/public/logo.svg";
import { cn } from "@/lib/utils";
import { useParams } from "next/navigation";
export default function Navbar() {
const [scroll, isScroll] = useState(false);
const routes = [
{
href: `/#home`,
label: "Home",
active: window.location.hash === "#home",
},
{
href: `/#about`,
label: "About",
active: window.location.hash === `#about`,
},
{
href: `/#services`,
label: "Services",
active: window.location.hash === `#services`,
}, {
href: `/#team`,
label: "Team",
active: window.location.hash === `#team`,
}, {
href: `/#contact`,
label: "Contact",
active: window.location.hash === `#contact`,
},
];
useEffect(() => {
if (typeof window !== "undefined") {
window.addEventListener("scroll", () =>
isScroll(window.scrollY > 900)
);
}
}, []);
return (
<motion.nav initial={{ opacity: 0, translateY: -100 }}
animate={{ opacity: 1, translateY: 0 }}
transition={{ duration: 0.5 }}
className={`fixed justify-center w-full top-0 left-0 p-6 items-center flex z-[1000] ${scroll ? "bg-[#141414]/50" : "bg-transparent"} transition duration-300`}
>
<Link
href="/#home"
className="hidden lg:block absolute left-0 m-[1em] py-2"
>
<Image src={Logo} className="h-24 w-full mr-3" alt="Logo" />
</Link>
<div className="items-center justify-between hidden w-full md:flex md:w-auto md:order-1">
<ul className="hidden lg:flex flex-col uppercase p-4 md:p-0 md:flex-row md:space-x-8 text-xl text-secondary">
{routes.map((route) => (
<Link
className={cn(
"hover:text-primary transition duration-300",
route.active
? "text-primary"
: "text-secondary"
)}
href={route.href}
>
{route.label}
</Link>
))}
</ul>
</div>
<Link
href="/schedule"
className="absolute p-3 rounded-sm text-center uppercase font-semibold tracking-wider bg-primary text-main z-605 right-[10px] sm:right-[40px] top-[20px] hover:bg-main transition-colors duration-150 hover:text-white"
>
MAKE AN APPOINTMENT
</Link>
</motion.nav >
);
};35 Replies
also your code is a bad idea
what you shld instead do, if you want to keep the code as is
wrap the routes array in useMemo
make a state variable, add this code
store the hash value in the state variable and use this state in the deps array of useMemo + using to check active links
const params = useParams();
useEffect(() => {
console.log("Hash:", window.location.hash);
}, [params]);store the hash value in the state variable and use this state in the deps array of useMemo + using to check active links
@Ojos Azules
lemme know if this didnt make sense lol
Ojos AzulesOP
it makes but i haven't used useMemo before
useMemo basically makes the code it wraps update only when its deps array is updated
your current code makes it so react makes the routes array on every rerender
Ojos AzulesOP
useMemo(() => {
const routes = [
{
href: `/#home`,
label: "Home",
active: window.location.hash === "#home",
},
{
href: `/#about`,
label: "About",
active: window.location.hash === `#about`,
},
{
href: `/#services`,
label: "Services",
active: window.location.hash === `#services`,
}, {
href: `/#team`,
label: "Team",
active: window.location.hash === `#team`,
}, {
href: `/#contact`,
label: "Contact",
active: window.location.hash === `#contact`,
},
];
}
, [params])something lke this? @Arinji
ye
wait
update this
window.location.hash ===
#contact,firstly make a state variable
so const [hash, setHash] = useState("");
in the place where you console log the hash from the useEffect
set the state there
and make the deps array have the state variable
Ojos AzulesOP
@Arinji
"use client";
import { useEffect, useMemo, useState } from "react";
import { motion } from 'framer-motion';
import Link from "next/link";
import Image from "next/image";
import Logo from "@/public/logo.svg";
import { cn } from "@/lib/utils";
import { useParams } from "next/navigation";
export default function Navbar() {
const params = useParams();
const [scroll, isScroll] = useState(false);
const [hash, setHash] = useState("");
useEffect(() => {
setHash(window.location.hash)
}, [params])
const routes = useMemo(() => [
{
href: `/#home`,
label: "Home",
active: window.location.hash === hash,
},
{
href: `/#about`,
label: "About",
active: window.location.hash === hash
},
{
href: `/#services`,
label: "Services",
active: window.location.hash === hash,
},
{
href: `/#team`,
label: "Team",
active: window.location.hash === hash,
},
{
href: `/#contact`,
label: "Contact",
active: window.location.hash === hash,
},
], [hash]);
useEffect(() => {
if (typeof window !== "undefined") {
window.addEventListener("scroll", () =>
isScroll(window.scrollY > 900)
);
}
}, []);
return (
<motion.nav initial={{ opacity: 0, translateY: -100 }}
animate={{ opacity: 1, translateY: 0 }}
transition={{ duration: 0.5 }}
className={`fixed justify-center w-full top-0 left-0 p-6 items-center flex z-[1000] ${scroll ? "bg-[#141414]/50" : "bg-transparent"} transition duration-300`}
>
<Link
href="/#home"
className="hidden lg:block absolute left-0 m-[1em] py-2"
>
<Image src={Logo} className="h-24 w-full mr-3" alt="Logo" />
</Link>
<div className="items-center justify-between hidden w-full md:flex md:w-auto md:order-1">
<ul className="hidden lg:flex flex-col uppercase p-4 md:p-0 md:flex-row md:space-x-8 text-xl text-secondary">
{routes.map((route, index) => (
<Link
key={index}
className={cn(
"hover:text-primary transition duration-300",
route.active
? "text-primary"
: "text-secondary"
)}
href={route.href}
>
{route.label}
</Link>
))}
</ul>
</div>
<Link
href="/schedule"
className="absolute p-3 rounded-sm text-center uppercase font-semibold tracking-wider bg-primary text-main z-605 right-[10px] sm:right-[40px] top-[20px] hover:bg-main transition-colors duration-150 hover:text-white"
>
MAKE AN APPOINTMENT
</Link>
</motion.nav >
);
};but now everything is active
oh
wait
...
Ojos AzulesOP
i think its ready
const params = useParams();
const [scroll, isScroll] = useState(false);
const [hash, setHash] = useState("");
useEffect(() => {
setHash(window.location.hash)
}, [params])
const routes = useMemo(() => [
{
href: `/#home`,
label: "Home",
active: hash === "#home",
},
{
href: `/#about`,
label: "About",
active: hash === "#about"
},
{
href: `/#services`,
label: "Services",
active: hash === "#services",
},
{
href: `/#team`,
label: "Team",
active: hash === "#team",
},
{
href: `/#contact`,
label: "Contact",
active: hash === "#contact",
},
], [hash]);Ojos AzulesOP
perfectly
thank you so much
Awesome, mark a solution :D
Answer
Ojos AzulesOP
and for the useMemo, didnt know it