Next.js Discord

Discord Forum

Active Links on click navbar

Answered
Ojos Azules posted this in #help-forum
Open in Discord
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:

"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 >
  );
};
Answered by Arinji
Awesome, mark a solution :D
View full answer

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
 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]);
you are still doing window.location.hash...
yea better
works now?
Ojos AzulesOP
perfectly
thank you so much
Awesome, mark a solution :D
Answer
Ojos AzulesOP
and for the useMemo, didnt know it