Hamburger menu will not close when LINK is clicked REACT - reactjs

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

Related

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";

How to make hamburger menu dissapear after clicking link with react scroll

I am trying to make my hamburger menu work with react scroll. It works as it should but when i click the links the menu does not dissapear. I tried the below but it makes it worst.
import React from 'react';
// import nav data
import { navData } from '../data';
// import components
import Socials from './Socials';
import { Link} from "react-scroll";
import { useState } from 'react';
const NavMobile = () => {
// destructure nav data
const [isActive, setIsActive] = useState(true)
const { items } = navData;
return (
<nav className={`${isActive? "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 (
<Link
activeClass="active"
to={item.href}
spy={true}
smooth={true}
offset={-70}
duration={500}
>
<li key={index} onClick={setIsActive(!isActive)}>
<a className='text-2xl font-primary uppercase' href={item.href} >
{item.name}
</a>
</li>
</Link>
);
})}
</ul>
<div className='text-2xl'>
<Socials />
</div>
</nav>
);
};
export default NavMobile;
I just can't find out what's the problem
Your conditional classNames for your <nav> don't need to be wrapped in the template literal ${} the line is instead written as follows
<nav className={isActive ? "w-full h-full flex flex-col justify-evenly overflow-hidden" : ""}>
Your onClick handler should also have it's own defined function.
const handleClick = () => {setIsActive(!isActive)}
And then be parsed to the onClick method of the <li>
<li key={index} onClick={handleClick}>
See https://bobbyhadz.com/blog/react-onclick-pass-event-and-parameter#:~:text=To%20pass%20an%20event%20and,event%20and%20parameter%20to%20handleClick%20. for more help. This is necessary because you are parsing the function to the onClick method rather than the result of the function.

Lazy loading element in typescript hook not working

I've implemented a lazy loading hook, but for some reason the carousel component is not loading at all. I've got it from here: https://betterprogramming.pub/lazy-loading-in-next-js-simplified-435681afb18a
This is my child that I want to render when it's in view:
import { useRef } from "react";
import Carousel from "../components/carousel";
import useOnScreen from "../hooks/useOnScreen";
// home page
function HomePage() {
const carouselRef = useRef();
const carouselRefValue = useOnScreen(carouselRef);
return (
<div className="snap-y">
{/* 1 */}
<div className="flex justify-center items-start relative min-h-[calc(100vh_-_5rem)] bg-black snap-start ">
{/* Cone */}
<div className="absolute w-full max-w-full overflow-hidden min-w-fit cone animate-wiggle"></div>
<div className="grid justify-center grid-cols-4 max-w-7xl">
//Content
</div>
{/* 2 */}
<div className="z-20 pb-4 bg-black snap-start" ref={carouselRef.current}>
{carouselRefValue && <Carousel />}
</div>
{/* 3 */}
<div className="flex items-start justify-center py-32 min-h-fit bg-slate-50 snap-start">
//More content
</div>
);
}
export default HomePage;
useOnScreen hook:
import { useState, useEffect } from "react";
const useOnScreen = (ref: any) => {
const [isIntersecting, setIntersecting] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => setIntersecting(entry.isIntersecting));
if (ref.current) {
observer.observe(ref.current);
}
}, []);
return isIntersecting;
};
export default useOnScreen;
Edit: Needed to add const carouselRef = useRef() as React.MutableRefObject<HTMLInputElement>; paired with #asynts's answer.
This is incorrect:
<div className="z-20 pb-4 bg-black snap-start" ref={carouselRef.current}></div>
it should be:
<div className="z-20 pb-4 bg-black snap-start" ref={carouselRef}></div>

Nextjs navbar active class only becomes active on a second click

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>
))}

Stop vertical scroll when modal is open React

I have a navigation bar in functional React with a mobile menu in it. The issue is that I have an animation that causes the navigation to hide unless the user scrolls up. When the navigation bar hides with this animation it drags mobile menu with it making it impossible to close the menu without being directed to a different page.
I would like it so that when the mobile menu is open scrolling is disabled until the user moves to a new page or closes the menu. Avoiding the issue of the navigation bar disappearing.
I have tried using in the event listener document.body.style.overflow = "hidden"; However this make the page stop scrolling regardless of whether the menu is open.
The scroll animation is triggering using an event listener and useEffect hook so I need a way of working it into this event listener so that when the menu is visible scroll can be disabled.
Heres the component:
import * as React from 'react';
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Squash as Hamburger } from 'hamburger-react';
import { debounce } from './utilities/helpers';
const links = [
{name: "Home", path: "/"},
{name: "Projects", path: "/projects"},
{name: "Contact", path: "/contact"}
];
export default function Header() {
//Navbar scroll hide
const [prevScrollPos, setPrevScrollPos] = useState(0);
const [visible, setVisible] = useState(true);
const handleScroll = debounce(() => {
const currentScrollPos = window.pageYOffset;
setVisible((prevScrollPos > currentScrollPos && prevScrollPos - currentScrollPos > 75) || currentScrollPos < 10); //Check if user has scrolled beyond the navbar height to hide or less then 10 to show navbar
setPrevScrollPos(currentScrollPos); //Sets last scroll position
}, 100); //Time interval for debounce
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [prevScrollPos, visible, handleScroll]);
//Hamburger state
const [isOpen, setOpen] = useState(false);
//Set navbar background without scroll
function ToggleNavLinks() {
var navBar = document.getElementById('navbar');
navBar.style.background = '#181a1d';
};
//Scroll event
window.addEventListener('scroll', function() {;
var navBar = document.getElementById('navbar');
//If user scrolls 1 or more pixels
if(window.scrollY >= 1){
navBar.style.background = '#181a1d';
navBar.style.boxShadow = 'box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)';
}
//Else they haven't scrolled
else {
navBar.style.background = 'none';
navBar.style.boxShadow = 'none'
}
});
return (
<nav style={{ top: visible ? '0' : '-75px' }} id="navbar" className="w-full h-[75px] fixed py-2 px-3 sm:px-6 flex justify-between items-center z-20 transition-all duration-300 ease-in-out">
{/* Logo */}
<Link to="/">
<div className="bg-[url('../public/Images/logo.png')] hover:bg-[url('../public/Images/logo-hover.png')] w-[260px] h-[60px]"></div>
</Link>
<div className="flex"> {/* Container for links and menu */}
{/* Hamburger */}
<div className="lg:hidden mb-0.5 z-10" onClick={(ToggleNavLinks)}> {/* Had to use parent element as Tailwind doesn't effect custom React components */}
<Hamburger
color="#ffffff"
easing="ease-in-out"
rounded
toggled={isOpen}
toggle={setOpen}
/>
</div>
{/* If menu is open */}
{isOpen && (
<>
<div className="h-screen w-screen absolute mt-[3.75rem] left-0 bg-black opacity-60 blur-sm"></div>
<ul className="h-screen w-3/5 sm:w-1/2 md:w-2/5 bg-quaternary flex flex-col lg:flex-row items-center absolute top-0 right-0 pt-32">
{links.map((link) => (
<li className="m-5 lg:my-0 lg:mx-5 leading-10">
<Link
className="text-2xl font-medium text-white hover:text-primary transition duration-300 ease-in-out"
to={link.path}
onClick={() => setOpen(false)}
>
{link.name}
</Link>
</li>
))}
</ul>
</>
)}
{/* Links */}
<ul className= "hidden lg:flex flex-row">
{links.map((link) => (
<li className="m-5 lg:my-0 lg:mx-5">
<Link className="text-2xl font-medium text-white hover:text-primary transition duration-300 ease-in-out" to={link.path}>
{link.name}
</Link>
</li>
))}
</ul>
</div>
</nav>
);
}
Heres the current bug:
Heres how its intended too look with scroll locked:
enter image description here

Resources