Hide full screen navigation when BrowserRouter Link is clicked - reactjs

I'm setting up a navigation for a react app that I'm currently working on.
I have a full screen navigation which overlays the website when a button in the header is clicked.
I've used useState to toggle the overlay on and off which is working great but I need to find a way to automatically hide the navigation overlay when a page link within the nav is clicked.
This is my Nav component:
import React, { useState } from 'react';
import { NavLink as Link } from 'react-router-dom';
function Navigation(){
const[showMenu, setShowMenu] = useState(false)
let menu
if(showMenu){
menu =
<nav>
<ul>
<Link to="/" exact activeStyle={{color: 'black'}}><li>Home</li></Link>
<Link to="/about"><li>About</li></Link>
<Link to="/work"><li>Work</li></Link>
<Link to="/hire"><li>Hire Me</li></Link>
</ul>
</nav>
}
return(
<>
<button className="nav-btn" onClick={() => setShowMenu(!showMenu)}>Menu</button>
{menu}
</>
);
}
export default Navigation;
I've tried adding onClick to the links like this:
<Link onClick={() => useState(false)} exact activeStyle={{color: 'black'}} to="/">
<li>Home</li>
</Link>
but I just get a compiler error "React Hook "useState" cannot be called inside a callback." So That's me out of ideas.
Any guidance would be greatly appreciated, as I'm learning React as I go.

useState has to be executed from a method. Simply create a method and set useState inside it.
function navigate(){
useState(false)
}
. . . . . . .
<Link onClick={navigate} exact activeStyle={{color: 'black'}} to="/">
<li>Home</li>
</Link>

Managed to figure it out.
I was right with the OnClick listener, but not quite there with the rest.
<Link onClick={() => setShowMenu(false)} to="/" exact activeStyle={{color: 'black'}}>
<li>Home</li>
</Link>

Related

NextJs 13 Experimental App Dir Hash in routes not directing to the hash id

I am using Nextjs 13 with the experimental App Dir but am not sure if this problem I am facing has anything to do with the issue I am facing. I have an id in my home page of "faqs" and when I click on the link, I can see it successfully goes to that link but does nothing in the browser. If I am on another page, I click the link and it takes me to the home page with the correct url but still stays on the top of the page and does not scroll to the indicated id. I did implement scroll={false} as suggested in the documentation but it makes no difference.
Here is a snippet of the relevant code parts:
"use client"
import React, { useState } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
const Navigation = () => {
const router = useRouter()
...
In the return:
<Link scroll={false} href="/#faqs">FAQS</Link>
I Even tried:
<button type="button" onClick={() => router.push("/#faqs")}>FAQS</button>
In React the hash works fairly well but in next js, even only in client rendering it seems convoluted. If anyone knows what I am doing wrong or if there is a viable work around, I would sure appreciate it.
Thank you in advance.
If I am missing anything, please let me know.
I use hashtags a lot and I plan to start using the app directory in future projects, so I dug into this and it's not pretty. Apparently, NextJS uses a different package for app directory components client-side called "next/navigation". It's very different from "next/router". Also, when using "next/link" elements, NextJS does not trigger the onRouteChangeComplete event when location.hash changes but location.pathname does not.
So, in order to detect a hash change and scroll to the associated element, I finally had to implement this hack:
"use client"
import { Inter } from '#next/font/google'
import paragraph from './paragraph'
import Link from 'next/link'
import { useEffect, useState } from 'react'
const inter = Inter({ subsets: ['latin'] })
export default function Home() {
const [navClick, setNavClick] = useState(false);
useEffect(() => {
setTimeout(() => {
const hash = window.location.hash;
if (hash) document.querySelector(hash).scrollIntoView();
}, 0);
}, [navClick])
const toggleNavClick = () => setNavClick((oldVal) => !oldVal);
return (
<main>
<nav>
<ul>
<li>
<Link href="/#one" onClick={toggleNavClick}>Section One</Link>
</li>
<li>
<Link href="/#two" onClick={toggleNavClick}>Section Two</Link>
</li>
<li>
<Link href="/#three" onClick={toggleNavClick}>Section Three</Link>
</li>
</ul>
</nav>
<div className="container">
<section id="one">
<h1>Section One</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="two">
<h1>Section Two</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="three">
<h1>Section Three</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
</div>
</main>
)
}
Since the hash change cannot be detected because no event is triggered, I basically created an event by toggling navClick each time a link is clicked. The navigation logic is enclosed in setTimeout() function because it triggers after window.location is updated.
Repo: https://github.com/designly1/next-hash-test
Demo: https://next-hash-test.vercel.app/

Change parent component variable from child component when using routing in react

I am trying to update parent components title from child components url. But the child components are loaded as navigation
Here is my Sandbox
https://codesandbox.io/s/react-typescript-forked-z1ijxi
Here is my layout.tsx
import { createContext, useContext, useState } from "react";
import { Outlet, Link } from "react-router-dom";
const Layout = () => {
const title = "";
return (
<>
<h2>{title}</h2>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/blogs">Blogs</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
</nav>
<Outlet />
</>
);
};
export default Layout;
Here I need to change the {title} when each of the child component loads
I tried to follow this answer . But I was not able to accomplish that.. Sorry very new in React
I would recommend using React Redux in your project. This library adds a variable namespace that is available to every component. From this global namespace, you could define a title variable that is accessible to the Layout component and editable by any other component no matter where it is.
Redux takes some time to learn, but it's very useful in all kinds of projects. This article has more information on the concept of Redux, though the exact implementation is a little outdated.

**Edited** NextJS - Use of OutsideClickHandler on a Burger/Dropdown menu - getting both the OutsideClickHandler AND the Links working

I have a project in NextJS, where I'm making the NavBar component.
In wide screen it's got the links along the top, and in mobile there's a burger menu which opens a dropdown
Up to this point it all works great, links work.
Then I thought it'd be nice to add an OutsideClickHandler for when it's in mobile mode - i wired in https://www.npmjs.com/package/react-outside-click-handler
but with this addition, the links for my dropdown no longer work - effectively the whole screen (aside from my burger) is being 'listened to' at triggering the OutsideClick.
I'm extremely new to all this. I figure it's because it's just listening to the burger icon. But I have no idea where to start to remedy this, and would super appreciate some input! Half a thought being to apply the OutsideClick handler to the SideMenu component somehow, but I'm puzzled because it's logic otherwise is in NavBar.
Thanks so much.
My apologies I can't link to github as I'm on a branch and my mentor is busy on a tech interview and I don't want to pull request it yet!
NavBar.js - without OutsideClickHandler - links on Side Menu work fine
import { useState } from "react";
import Link from "next/link";
import styles from "../NavBar/NavBar.module.css";
import ButtonBase from "../button/buttonPrimary";
import SideMenu from "../SideMenu/SideMenu";
import MenuItems from "../../../data/MenuItems/MenuItems";
const NavBar = () => {
const [menuIsOpen, setMenuIsOpen] = useState(false);
const toggleMenu = () => {
if (menuIsOpen === false) {
setMenuIsOpen(true);
} else {
setMenuIsOpen(false);
}
};
return (
<nav className={styles.navBarBox}>
<div className={styles.menuIcon}>
<i className={"fas fa-bars fa-2x"} onClick={toggleMenu}></i>
</div>
<div className={styles.mobileSideMenu}>
{menuIsOpen ? <SideMenu /> : <div></div>}
</div>
<ul className={styles.navMenu}>
{MenuItems.map((item) => (
<li className={styles.navItem} key={item.id}>
<Link href={item.path}>{item.title}</Link>
</li>
))}
</ul>
<Link href="/contact-me">
<a className={styles.contactMeWeb}>
<ButtonBase label="Contact Me" isPrimary={false}></ButtonBase>
</a>
</Link>
<Link href="/contact-me">
<a className={styles.contactMeMobile}>
<i className={"fa-solid fa-message fa-2x"}> </i>
</a>
</Link>
</nav>
);
};
NavBar.js - with OutsideClickHandler - OutsideClickHandler works, but links in side menu only act as an outside click
import { useState } from "react";
import Link from "next/link";
import OutsideClickHandler from "react-outside-click-handler";
import styles from "../NavBar/NavBar.module.css";
import ButtonBase from "../button/buttonPrimary";
import SideMenu from "../SideMenu/SideMenu";
import MenuItems from "../../../data/MenuItems/MenuItems";
const NavBar = () => {
const [menuIsOpen, setMenuIsOpen] = useState(false);
return (
<nav className={styles.navBarBox}>
<OutsideClickHandler
onOutsideClick={() => {
setMenuIsOpen(false);
}}
>
{/* BURGER ICON */}
<a
className={styles.menuIcon}
onClick={() => setMenuIsOpen(!menuIsOpen)}
>
<i className={"fas fa-bars fa-2x"}></i>
</a>
</OutsideClickHandler>
{/* SIDE MENU */}
{menuIsOpen && (
<div className={styles.mobileSideMenu}>
<SideMenu />
</div>
)}
<ul className={styles.navMenu}>
{MenuItems.map((item) => (
<li className={styles.navItem} key={item.id}>
<Link href={item.path}>{item.title}</Link>
</li>
))}
</ul>
<Link href="/contact-me">
<a className={styles.contactMeWeb}>
<ButtonBase label="Contact Me" isPrimary={false}></ButtonBase>
</a>
</Link>
<Link href="/contact-me">
<a className={styles.contactMeMobile}>
<i className="fa-regular fa-envelope fa-2x"></i>
</a>
</Link>
</nav>
);
};
export default NavBar;
SideMenu.js
import MenuItems from "../../../data/MenuItems/MenuItems";
import styles from "../SideMenu/SideMenu.module.css";
const SideMenu = () => {
return (
<div className={styles.mobileMenuBox}>
<ul className={styles.mobileMenu}>
{MenuItems.map((item) => (
<li className={styles.mobileItem} key={item.id}>
<Link href={item.path}><div>{item.title}</div></Link>
</li>
))}
</ul>
</div>
);
};
export default SideMenu;
SOLVED!
I just dragged the:
{menuIsOpen && (
<div className={styles.mobileSideMenu}>
<SideMenu />
</div>
)}
between the
<OutsideClickHandler></OutsideClickHandler>
tags

Why is my gatsby activeStyle only taking effect when I refresh the page?

I am hoping that someone could possibly help me.
I'm tryng to make my navigation links active in Gatsby. So I'm using Link with activeStyle. And for some reason it is not working. If I refresh the page the active style takes effect on the current page. But every time I change the page I have to refresh again.
import * as React from "react"
import { Link } from "gatsby"
import "../components/styles/navigation.module.css"
const Navigation = ({ menuLinks }) => {
return (
<header>
<div className="container">
<div>
<nav>
<ul>
{menuLinks.map(link => (
<li
key={link.name}
>
<Link activeStyle={{color: "black"}} className="nav-links" to={link.link}>
{link.name}
</Link>
</li>
))}
</ul>
</nav>
</div>
</div>
</header>
)
}
export default Navigation
This was a stupid mistake on my part. My NPM packages and Gatsby were extremely out of date. I updated NPM Packages and Gatsby and now it works like it should.

onClick event not firing on functional element

For the life of me I cannot understand why the click event is not firing in my functional component.
Here's my component:
import React, { useState, useEffect } from 'react';
import {
Collapse,
Navbar,
NavbarToggler,
Nav,
NavItem,
NavLink,
} from 'reactstrap';
import classNames from 'classnames';
import img from '../../img/logo/logo-white.svg';
const NavBar = () => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => {
console.log('toggllling', isOpen);
setIsOpen(!isOpen);
};
return (
<div
id="nav-wrap"
className={classNames({ 'bg-dark-blue': isOpen })}
>
<Navbar color="transparent" dark expand="lg" className="container">
<a className="navbar-brand" href="/">
<img alt="logo light" src={img} />
</a>
<NavbarToggler onClick={toggle} />
<Collapse isOpen={isOpen} navbar>
<Nav className="navbar-nav" navbar>
<NavItem className="active">
<NavLink href="/">First</NavLink>
</NavItem>
<NavItem>
<NavLink href="/">second</NavLink>
</NavItem>
<NavItem>
<NavLink href="/">Thord</NavLink>
</NavItem>
<NavItem className="separated">
<NavLink href="/">Fourth</NavLink>
</NavItem>
<NavItem>
<NavLink href="/">fifth</NavLink>
</NavItem>
<NavItem>
<NavLink href="/" className="btn btn-light">last</NavLink>
</NavItem>
</Nav>
</Collapse>
</Navbar>
</div>
);
};
export default NavBar;
It's a bit more advanced version of the navbar toggle example from the reactstrap page: https://reactstrap.github.io/components/navbar/
I can see from the React Devtools that the event is bound to the correct react component. If I replace toggle with something that should fire immediately like a function call, then it fires immediately. Yet when I click on the actual button, nothing happens. I even bound a click event to the document to see if there is something hidden above the button and logged out if the event propagates down to correct element - it did. so for the life of me, I cannot figure out why the toggle function never fires and why I never see 'toggllling' in my console. I've tried adding this onClick to any of the other elements in the component also - and it never works anywhere inside the component.
It's probably something really small, that I'm missing here, but for the life of me - I cannot figure out what it is exactly. Not sure what other bits of information could be useful here. Perhaps how I use the component:
ready(() => {
// Example:
const wrap = document.getElementById('nav-wrap');
if (wrap) {
// perform element replacement
const parent = wrap.parentNode;
const temp = document.createElement('div');
render(<NavBar element={wrap} />, temp);
parent.replaceChild(temp.querySelector('div#nav-wrap'), wrap);
}
});
Edit:
I simplified the component and results are the same - the event is not firing. When I debug it with browser devtools and add breakpoint at mouse click, then I can see that function call ends up in react-dom.development.js function noop() {} Why is that?
Have you tried a setState with a callback?
setIsOpen((prevState) => (
!prevState
));
Not sure how useful this answer is to the general population. There are not of onClick "not working" questions. Mods - if you see nothing to learn here, please just mark the question for deletion.
So what happened here was that I had this bit of a relic replacement mechanic in my index.js which created the component. I rendered the component inside element not in DOM:
const temp = document.createElement('div');
render(<NavBar element={wrap} />, temp);
And then looked it up and replaced its pure HTML predecessor with the react component. I went for this complex solution, because initially, I just tried to reuse some components. I later changed this logic, but did not go back to just using render(<NavBar/>, wrap);, which would have saved me all the trouble of writing this question.
So my own complexity, which I did not reduce on time, created even more overhead and wasted time. So yeah - avoid complexity. That's the lesson here.

Resources