Nextjs navbar active class only becomes active on a second click - reactjs

I'm trying to create header component for next.js/tailwindcss app. The nav active class only shows active when clicked on a second time. I'd like for it to be active upon the first click. Where am I going wrong? What element should I target with tailwindcss to reflect active state?
navlink.js file:
import Link from 'next/link';
const NavItem = ({ text, href, active }) => {
return (
<Link href={href}>
<a
className={`nav__item ${
active ? 'active underline underline-offset-8' : ''
}`}
>
{text}
</a>
</Link>
);
};
export default NavItem;
header.js :
import Image from 'next/image';
import React, { useState } from 'react';
import NavItem from './NavItem';
const MENU_LIST = [
{ text: 'About', href: '/about' },
{ text: 'My Work', href: '/MyWork' },
{ text: 'Blog', href: '/blog' },
{ text: 'Contact', href: '/contact' },
];
const Header = () => {
const [navActive, setNavActive] = useState(null);
const [activeIdx, setActiveIdx] = useState(-1);
return (
<header className="bg-white">
<nav className="max-w-5xl mx-auto border border-top-gray">
{/*containment div*/}
<div className="flex justify-between">
{/*Logo Container*/}
<div className="cursor-pointer">
<a href="/">
<Image
src="/../public/images/soulLogo.webp"
alt="site logo"
width={233}
height={144}
/>
</a>
</div>
{/*Link Container*/}
<div className="hidden md:flex">
<div
onClick={() => setNavActive(!navActive)}
className={`nav__menu-bar md:underline underline-offset-8 decoration-black `}
></div>
<div
className={`${
navActive
? 'active underline underline-offset-8 decoration-black'
: ''
} nav__menu-list flex items-center space-x-8 text-gray-700 tracking-wider `}
>
{MENU_LIST.map((menu, idx) => (
<div
onClick={() => {
setActiveIdx(idx);
setNavActive(false);
}}
key={menu.text}
>
<NavItem active={activeIdx === idx} {...menu} />
</div>
))}
</div>
</div>
{/*Mobile Menu button*/}
<div className="flex relative flex-col gap-y-2 cursor-pointer pt-14 pr-3 md:hidden">
<div className="w-24 h-1 bg-black shadow-gray-700 rounded"></div>
<div className="w-24 h-1 bg-black shadow-gray-700 rounded"></div>
<div className="w-24 h-1 bg-black shadow-gray-700 rounded"></div>
</div>
{/*Mobile Menu*/}
<
</div>
</nav>
</header>
);
};
export default Header;

I think we used the same code and I ran into the same problem.
I fixed it with with using next/router and comparing the paths inside the NavItem.js component.
navitem.js
import Link from "next/link";
import { useRouter } from 'next/router';
const NavItem = ({ href, text }) => {
const router = useRouter();
const currentRoute = router.pathname;
return (
<Link href={href} className={currentRoute === `${href}` ? 'active' : ''}> {text} </Link>
);
};
export default NavItem;
navbar.js
{PRIMARY_NAVIGATION_LIST.map((menu) => (
<div key={menu.text} >
<NavItem
href={menu.href}
text={menu.text}
/>
</div>
))}

Related

How to avoid closing DropDown on MouseOver?

I made Navigation component with dynamic menu items.
`
import React, { useState } from "react";
import NavMenuItems from "../data/NavMenuItems";
function NavBar() {
const [dropDown, setDropDown] = useState({});
const setDropDownOpen = (name) => {
setDropDown({[name]: true });
};
const setDropDownClose = (name) => {
setDropDown({[name]: false });
};
return (
<div className="flex flex-row my-2 mx-5">
{NavMenuItems.map((menu, index) => (
<>
<div key={menu.item} className="relative flex flex-col mx-1">
<div
className="bg-[#121C24] px-2 h-5 text-white text-sm hover:bg-green-700 hover:text-black hover:cursor-pointer "
onMouseEnter={() => setDropDownOpen(menu.item)}
onMouseLeave={() => setDropDownClose(menu.item)}
>
{menu.item}
</div>
{dropDown[menu.item] && (
<div className="bg-slate-200 absolute top-6 px-4 py-2"
onMouseEnter={() => setDropDownOpen(menu.item)}
onMouseLeave={() => setDropDownClose(menu.item)}
>
{menu.subitems.map((submenu, index) => (
<div key={index}>{submenu}</div>
))}
</div>
)}
</div>
</>
))}
</div>
);
}
export default NavBar;
NavMenuItems.js
`
const NavMenuItems = [
{
item: "Events",
subitems: ["Event1", "Event2", "Event2"],
},
{
item: "Reports",
subitems: ["Reports1", "Reports2", "Reports3"],
},
];
export default NavMenuItems
When i mouseover on tabs, its working fine. but when i move over dropdown sub menu, it closes and cant select anything in submenu items.
Can someone help with this?

How do I make tailwind scroll snap & smooth scroll work together in React?

I am trying to build a single page react app and I was wondering how I would apply tailwind scroll snap and smooth scroll together. Every time I enable scroll-snap in the y-direction, my code for smooth scroll no longer works. I'm a beginner to React so any help would be greatly appreciated. Below I have attached the app.jsx, the header, and the first component.
This is the closest stackoverflow post I've found: Making smooth anchor tag scrolling compatible with scroll snapping
import React from "react";
import Header from "./components/Header"
import First from "./components/First";
import Second from "./components/Second";
import Third from "./components/Third";
function App() {
return (
<div className="overflow-y-auto snap-y snap-mandatory h-[100vh] w-[100%]">
<Header />
<First />
<Second />
<Third />
</div>
);
}
export default App;
import React from "react";
const First = () => {
return (
<div
className="w-full h-screen flex justify-center items-center bg-blue-400 snap-center"
link="first"
>
First
</div>
);
};
export default First;
import React, { useState } from "react";
import { FaBars, FaTimes } from "react-icons/fa";
import { Link } from "react-scroll";
const Header = () => {
const [nav, setNav] = useState(false);
const links = [
{
id: 1,
link: "first",
},
{
id: 2,
link: "second",
},
{
id: 3,
link: "third",
},
];
return (
<div className="flex justify-between items-center w-full h-20 px-4 text-white bg-black fixed">
<div>
<h1 className="text-5xl font-signature ml-2">Practice Youtuber Tutorial</h1>
</div>
<ul className="hidden md:flex">
{links.map(({ id, link }) => (
<li
key={id}
className="px-4 cursor-pointer capitalize font-medium text-gray-500 hover:scale-105 duration-200"
>
<Link to={link} smooth duration={500}>
{link}
</Link>
</li>
))}
</ul>
<div
onClick={() => setNav(!nav)}
className="cursor-pointer pr-4 z-10 text-gray-500 md:hidden"
>
{nav ? <FaTimes size={30} /> : <FaBars size={30} />}
</div>
{nav && (
<ul className="flex flex-col justify-center items-center absolute top-0 left-0 w-full h-screen bg-gradient-to-b from-black to-gray-800 text-gray-500">
{links.map(({ id, link }) => (
<li
key={id}
className="px-4 cursor-pointer capitalize py-6 text-4xl"
>
<Link
onClick={() => setNav(!nav)}
to={link}
smooth
duration={500}
>
{link}
</Link>
</li>
))}
</ul>
)}
</div>
);
};
export default Header;
smooth scroll use by adding the following code
import { Link, animateScroll as scroll } from "react-scroll";

Hamburger menu will not close when LINK is clicked REACT

I built a mobile nav hamburger dropdown. The NavMobile links are added to the dropdown in a seperate component. When I try to click on a link in the dropdown it will scroll to the proper section of the page but the mobile dropdown will have to be manually closed which is not what I was hoping for.
Here is the code in the header component:
import React, { useState, useEffect } from "react";
// import data
import { headerData } from "../data";
//import components
import Nav from "./Nav";
import NavMobile from "./NavMobile";
import Socials from "./Socials";
//import icons
import { TiThMenu } from "react-icons/ti";
const Header = () => {
//destructure header data
const { logo } = headerData;
//header state
const [isActive, setIsActive] = useState(false);
//nav mobile state
const [navMobile, setNavMobile] = useState(false);
//scroll event
useEffect(() => {
window.addEventListener("scroll", () => {
window.scrollY > 50 ? setIsActive(true) : setIsActive(false);
});
});
return (
<header
className={`${
isActive ? "h-[100px] lg:h-[110px] shadow-lg" : "h-[120px] lg:h-[150px]"
} fixed bg-white left-0 right-0 z-10 max-w-[1920px] w-full mx-auto transition-all duration-300`}
>
<div className="flex justify-between items-center h-full pl-[50px] pr-[60px]">
{/* logo */}
<a href="/">
<img className="w-[188px] h-[90px]" src={logo} alt="Tara Zep Logo" />
</a>
{/* nav hidden mobile*/}
<div className="hidden xl:flex">
<Nav />
</div>
{/* nav menu btn hidden desktop*/}
<div
onClick={() => setNavMobile(!navMobile)}
className="xl:hidden absolute right-[5%] bg-white text-dark p-2 rounded-md cursor-pointer"
>
<TiThMenu className="text-3xl" />
</div>
{/* nav mobile */}
<div
className={`${navMobile ? "max-h-full" : "max-h-0"} ${
isActive
? "top-[100px] lg:top-[110px]"
: "top-[120px] lg:top-[150px]"
} fixed bg-white w-full h-full left-0 -z-10 transition-all duration-300`}
>
<NavMobile onClick={() => setNavMobile(!navMobile)}/>
</div>
{/* social icons hidden show on desktop */}
<div className="hidden xl:flex">
<Socials />
</div>
</div>
</header>
);
};
export default Header;
And the NavMobile component:
import React from "react";
//import Link
import { Link } from "react-scroll";
//import nav data
import { navData } from "../data";
//import components
import Socials from "./Socials";
const NavMobile = () => {
//destructure nav data
const { items } = navData;
return (
<nav className="w-full h-full flex flex-col justify-evenly overflow-hidden">
<ul className="flex flex-col justify-center items-center gap-y-6 py-6 mb-8">
{items.map((item, index) => {
return (
<li key={index}>
<Link
to={item.href}
spy={true}
smooth={true}
offset={-70}
duration={500}
className="text-2xl font-primary cursor-pointer uppercase"
>
{item.name}
</Link>
</li>
);
})}
</ul>
<div className="text-2xl">
<Socials />
</div>
</nav>
);
};
export default NavMobile;
The problem is that when I try to add the
onClick={() => setNavMobile(!navMobile)} to the <NavMobile /> tag it does not work to collapse the dropdown. When i add it to the surrounding div it will work everywhere EXCEPT for on the clicked links. I am using React Scroll for the links.
you may try passing setNavMobile funcion to NavMobile as a prop and then calling it on Link onClick

Next.js: Update Navbar component when navigating to new route

Goal: Update Navbar accent when page is navigated to
Current: The Navbar accent has a delayed update. It updates to the last visited page.
For example: if I start on page1, then click on page2, the accent remains on page1 until I click on page2 again. If I then click on page3, page2 will now have the accent.
Attempted: I am using next.router in useEffect to try to update the accent on change of router.
Code:
app.js
import "tailwindcss/tailwind.css";
import Layout from "../components/Layout";
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
export default MyApp;
layout.js
import Navbar from "./Navbar";
export default function Layout({ children }) {
return (
<div className="layout">
<Navbar />
<div className="">{children}</div>
</div>
);
}
navbar.js
import Link from "next/link";
import { useRouter } from "next/router";
import { useEffect } from "react";
function classNames(...classes) {
return classes.filter(Boolean).join(" ");
}
const navigation = [
{ name: "page1", href: "/", current: true },
{ name: "page2", href: "/page2", current: false },
{ name: "page3", href: "/page3", current: false },
];
export default function Navbar() {
const router = useRouter();
useEffect(
(e) => {
navigation.map((item) => {
if (router.route == item.href) {
item.current = true;
} else {
item.current = false;
}
});
console.log(router.route);
},
[router]
);
return (
<div>
<div>
{navigation.map((item) => (
<Link href={item.href} key={item.name}>
<a
className={classNames(
item.current
? "border-indigo-500 text-gray-900"
: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700",
"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium"
)}
aria-current={item.current ? "page" : undefined}
>
{item.name}
</a>
</Link>
))}
</div>
</div>
);
}
index.js page2 & page3 are similar.
export default function Home() {
return (
<div className="flex flex-col items-center justify-center min-h-screen py-2">
Hello
</div>
);
}
Drop the useEffect and just go with
import Link from "next/link";
import { useRouter } from "next/router";
function classNames(...classes) {
return classes.filter(Boolean).join(" ");
}
const navigation = [
{ name: "page1", href: "/" },
{ name: "page2", href: "/page2" },
{ name: "page3", href: "/page3" },
];
export default function Navbar() {
const router = useRouter();
return (
<div>
<div>
{navigation.map((item) => (
<Link href={item.href} key={item.name}>
<a
className={classNames("inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium", router.route === item.href ? "border-indigo-500 text-gray-900" : "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700")}
aria-current={router.route === item.href ? "page" : undefined}
>
{item.name}
</a>
</Link>
))}
</div>
</div>
);
}
Maybe using the item.current booleans in your return statement could help you. e.g.
because you have
{ name: "page1", href: "/", current: true },
and you're specifying that it's "current" value is true when clicked.
You could do something like this:
return (
<div>
<div>
{navigation.map((item) => (
{item.current===false
<Link href={item.href} key={item.name}>
<a
className={classNames(
item.current
? "border-indigo-500 text-gray-900"
: "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700",
"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium"
)}
aria-current={item.current ? "page" : undefined}
>
{item.name}
</a>
</Link>}
{item.current===true
*** WHATEVER YOU WAN'T TO BE DIFFERENT ***
}
))}
</div>
</div>
);
}

React Rotate Accordion Arrow Onclick

How can I rotate the arrows onclick
This is my code below for my react collapse and I would like to rotate the arrows of my collapse accordion when it clicked.
I'm using reactstrap to build it and react fontawsome for my Icons
I've also attached a .GIF Link to demonstrate how my accordion is currently functioning.
import React, { Component } from "react";
import { Collapse, CardBody, Card, CardHeader } from "reactstrap";
import { faChevronDown } from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
const nested = [
{
id: 10,
title:"Customer Queries"
},
];
const Icon = <FontAwesomeIcon icon={faChevronDown} color={"#EB8C00" } />;
class Colapse extends Component {
constructor(props) {
super(props);
this.tog = this.tog.bind(this);
this.state = {
col: 0,
cards: [
{
id:1,
title:"General information requests",
sub_title:"Fault reports",
body:"Something to display for body 1",
},
{
id:2,
title:"Account queries",
sub_title:"Communication protocols",
body:"Something to display for body 2",
}
] };
}
tog(e) {
let event = e.target.dataset.event;
this.setState({
col: this.state.col === Number(event) ? 0 : Number(event),
});
}
render() {
const { col, cards } = this.state;
return (
<div className="container collapse-container">
{nested.map((element, index) => {
return (
<Card key={index} className="card rounded ">
<CardHeader
className="card-header"
onClick={this.tog}
data-event={(index = element.id)}
>
<p className="collapse d-flex">
<div className=" p-2 mr-auto font-weight-bold">
<p className="">{element.title}</p>
</div>
<a
class="click-layer"
onClick={this.tog}
data-event={index}
></a>
</p>
</CardHeader>
<Collapse isOpen={col !== index}>
<CardBody className="card-body">
<p className="card-text">Customer Queries relate to information, account or meter related faults reported by the customer.</p>
{/**HERE NESTED */}
{cards.map((index) => {
return (
<Card key={index} className="card-sub rounded ">
<p className="card-header-sub" >{index.title}</p>
<CardHeader
className="card-header-sub"
onClick={this.tog}
data-event={index.id}
>
<p className="colle d-flex">
<div className="p-2 rotate">{Icon}</div>
<div className=" p-2 mr-auto font-weight-bold">
<p className="card-header-sub">{index.sub_title}</p>
</div>
<a
class="click-layer"
onClick={this.tog}
data-event={index.id}
></a>
</p>
</CardHeader>
<Collapse isOpen={col === index.id}>
<CardBody className="card-body-sub">
<p className="card-text-body">{index.body}</p>
</CardBody>
</Collapse>
</Card>
);
})}
{/**End Of Nested */}
</CardBody>
</Collapse>
</Card>
);
})}
</div>
);
}
}
export default Colapse;
Rotate the svg according to some condition e.g. col === element.id
<div className={`p-2 rotate ${col === element.id ? "isRotated" : ""}`}>{Icon}</div>
on your stylesheet:
.isRotated svg {
transform: rotate(-90deg);
}

Resources