I'm trying to make my navigation responsive, so clicking a burger menu icon will then add a class to the nav. Simple concept.
Still new to react and next js.
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useRef, setState, useEffect } from 'react'
const Nav = (props) => {
const router = useRouter()
const isExpanded = () => {
this.state = {
isExpanded: false
}
}
function handleToggle(e) {
e.preventDefault();
this.setState({
isExpanded: !this.state.isExpanded
});
}
return (
<nav>
<div>
<Link href="/">
<a><img src="/img/logo.svg" /></a>
</Link>
<button onClick={e => this.handleToggle(e)} className="nav-icon">
<svg className="fill-current text-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"/></svg>
</button>
</div>
<div>
<ul className={`collapsed ${isExpanded ? "is-expanded" : ""}`}>
<li className={router.pathname == "/" ? "active" : ""}>
<Link href="./">
<a>Home</a>
</Link>
</li>
<li className={router.pathname == "/blog" ? "active" : ""}>
<Link href="/blog">
<a>Blog</a>
</Link>
</li>
</ul>
</div>
</nav>
)
}
export default Nav
When I click on the menu burger icon, I then get
TypeError: Cannot read property 'handleToggle' of undefined
Any advice in the right direction would be greatly appreciated. Thank you
You are confusing class based and functional components and not leveraging the advantages of either. Here is a working snippet illustrating a minimal implementation.
It uses React.useState() to declare and set state. Also, when calling named functions event will be passed implicitly so you can simply declare onClick={handleToggle}.
p {
font-family: monospace;
}
<script src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<div id="App"></div>
<script type="text/babel">
const {useState} = React;
const Nav = () => {
const [isExpanded, setIsExpanded] = useState(false);
function handleToggle(e) {
setIsExpanded(prevState => !prevState);
}
return (
<nav>
<div>
<button type="button" onClick={handleToggle} className="nav-icon">
Burger
</button>
</div>
<p>{`isExpanded: ${isExpanded}`}</p>
</nav>
)
}
ReactDOM.render(<Nav />, document.getElementById('App'));
</script>
Related
I'm trying to make navigation menu similiar to the nav menu in reactjs.org
I'm using Header component and navigation which is objects with links and name. I'm adding class onClick using the state but this toggle all buttons.
import React, { useState } from "react";
import styles from "./index.module.css";
import getNavigation from "../../utils/navigation";
import { Link } from "react-router-dom";
import logo from "../../images/europa-logo.png";
const Header = () => {
const links = getNavigation();
const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive);
};
return (
<div>
<nav className={styles.topnav}>
<div className={styles.pageWrapper}>
<img src={logo} alt="Logo" />
<ul>
{links.map((l, i) => (
<li key={i}>
<Link
className={isActive ? "btn-active" : null}
onClick={toggleClass}
to={l.link}
value={l.title}
>
{l.title}
</Link>
</li>
))}
<li>
{" "}
<div className={styles.social}>
<a href="https://facebook.com">
<FontAwesomeIcon
size="2x"
icon={["fab", "facebook-square"]}
/>{" "}
</a>
<a href="mailto:someone#mail.com">
<FontAwesomeIcon size="2x" icon="envelope" />
</a>
</div>
</li>
</ul>
</div>
</nav>
</div>
);
};
export default Header;
The result is all buttons are activated:
My goal is to activate only the link which is clicked and the first button in nav menu need to be activated by default. What I'm doing wrong?
You can use <NavLink> instead of simple <Link>
is a special version of the that will add styling attributes to the rendered element when it matches the current URL.
<NavLink to="/" activeClassName="active">Link</NavLink>
You can check the docs here:
https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/docs/api/NavLink.md
You can define active index and your condition look like this
className={activeIndex === i ? "btn-active" : ""}
Toggle class function:
const [activeIndex, setActiveIndex] = useState(0);
const toggleClass = (i) => {
setActiveIndex(i);
};
and onClick will look like this
onClick={()=>{toggleClass(i);}}
I have a header component as a function component. I want show a popup when logo text is clicked. After for a time it should close automatically. I use hooks for state of popup. But set state function doesn't work in setTimeout function. How can fix this?
import Link from 'next/link'
import style from './header.module.css'
const Header = () => {
const [popupOpen, setPopupOpen] = React.useState(false)
return (
<header className={style.header}>
<nav className={style.nav}>
<div
className={style.popupContainer}
onClick={() => {
setPopupOpen(!popupOpen)
console.log(popupOpen)
setTimeout(() => {
console.log(popupOpen)
setPopupOpen(!popupOpen)
console.log(popupOpen)
}, 1000)
}}
>
<span className={style.logo}>Logo</span>
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
</div>
<ul className={style.ul}>
<li>
<Link href='/'>
<a>.home</a>
</Link>
</li>
<li>
<Link href='/contact'>
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
)
}
export default Header
Console log:
Let me suggests, this is the same question as:
React - useState - why setTimeout function does not have latest state value?
const _onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
setPopupOpen(popupOpen => !popupOpen)
}, 2000);
};
Its happening because setPopupOpen is asynchronous. So by the time setPopupOpen(!popupOpen) is called it has same value as onClick first setPopupOpen(!popupOpen) so eventually when it called both setPopup doing same state update i.e both updating as false. Better way is to usesetPopupOpen callback function to update the value. I added this code.
import { useState } from "react";
import Link from "next/link";
import style from "./style.module.css";
const Header = () => {
const [popupOpen, setPopupOpen] = useState(false);
const toggle = () => {
setPopupOpen((prev) => !prev);
};
const onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
toggle();
}, 1000);
};
return (
<header className={style.header}>
<nav className={style.nav}>
<div className={style.popupContainer} onClick={onClick}>
<span className={style.logo}>Logo</span>
{popupOpen && (
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
)}
</div>
<ul className={style.ul}>
<li>
<Link href="/">
<a>.home</a>
</Link>
</li>
<li>
<Link href="/contact">
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
);
};
export default function IndexPage() {
return (
<div>
<Header />
</div>
);
}
Here is the demo: https://codesandbox.io/s/pedantic-haibt-iqecz?file=/pages/index.js:0-1212
I am a total newbie on react. I am trying to use a functional app and wanted to use the function toggleBurgerMenu(). The problem, I am not sure what else I need to define it. Should I use props.toggleBurgerMenu()?
import React from 'react'
import { Link } from "react-router-dom";
import './Header.scss'
const Header = (props) => {
toggleBurgerMenu = () => {
document.querySelector('.navbar-menu').classList.toggle('is-active');
}
return (
<div>
<header>
<nav className="navbar" role="navigation" aria-label="main navigation">
<div id="navbarBasicExample" className="navbar-menu">
<div className="navbar-start">
<Link to="/home" className="navbar-item" onClick={()=> this.toggleBurgerMenu()}>Home</Link>
</div>
</div>
</nav>
</header>
</div>
)
}
export default Header
Appreciate any help. thank you
You need to define your toggleBurgerMenu function like this const toggleBurgerMenu = () => { document.querySelector('.navbar-menu').classList.toggle('is-active'); }. And then you can use it in the onClick Event like this <Link to="/home" className="navbar-item" onClick={toggleBurgerMenu}>Home</Link>
with react the best thing to do is not using document you could use a ref instead, but in this case you can add some state and update the classname
here is an easy example
const Header = (props) => {
/** function to set the state of the button */
const [isActive, setIsActive] = useState(initialState)
return (
<div>
<header>
<nav className="navbar" role="navigation" aria-label="main navigation">
{/**if its true we add is-active class there are several package to this*/}
<div id="navbarBasicExample" className={`navbar-menu ${isActive ? 'is-active' : ''}`}>
<div className="navbar-start">
<Link to="/home" className="navbar-item" onClick={() => setIsActive(prev => !prev.setIsActive)}>Home</Link>
</div>
</div>
</nav>
</header>
</div>
)
}
export default Header
I have been following a coding tutorial of making responsive Navbar from Youtube in Html,CSS and JS. While i really wanted to remake it again in react, the hamburger icon works flawlessly previously when i made it in plain Html, CSS and JS Click here to see the example. (Switch the browser in mobile view to see the Hamburger icon)
But when I copied all my code in react (as follows):
import React, { Component } from "react";
import "./style/navbar.css";
import { Link } from "react-router-dom";
class navbar extends Component {
render() {
const navslide = () => {
const burger = document.querySelector(".burger");
const nav = document.querySelector(".nav-links");
const navLinks = document.querySelectorAll(".nav-links li");
burger.addEventListener("click", () => {
nav.classList.toggle("nav-active");
navLinks.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = "";
} else {
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7 +
0.5}s`;
}
});
burger.classList.toggle("toggle");
});
};
return (
<div>
<nav>
<div className="logo">
<h3>College Facemash</h3>
</div>
<ul className="nav-links">
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/login">Login / Signup</Link>
</li>
</ul>
<div className="burger" onClickCapture={navslide}>
<div className="line1"></div>
<div className="line2"></div>
<div className="line3"></div>
</div>
</nav>
</div>
);
}
}
export default navbar;
it worked well but not not functioning in single click. Instead it did required Double click to function.
Click here to see the demo.(Switch the browser in mobile view to see the Hamburger icon)
So, What changes should i made in order to make my code work flawlessly
Your help would be really valuable to me.
Thanks...
Try like this:
class Navbar extends Component {
const navslide = () => {
const nav = document.querySelector(".nav-links");
const navLinks = document.querySelectorAll(".nav-links li");
nav.classList.toggle("nav-active");
navLinks.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = "";
} else {
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7 + 0.5}s`;
}
});
burger.classList.toggle("toggle");
});
};
render() {
return (
<div>
<nav>
<div className="logo">
<h3>College Facemash</h3>
</div>
<ul className="nav-links">
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/login">Login / Signup</Link>
</li>
</ul>
<div className="burger" onClick={() => navslide()}>
<div className="line1"></div>
<div className="line2"></div>
<div className="line3"></div>
</div>
</nav>
</div>
);
}
}
why calling burger.addEventListener("click") while your function will run on burger click! this is why it need 2 click to run!
import React, { Component } from "react";
import "./style/navbar.css";
import { Link } from "react-router-dom";
class navbar extends Component {
render() {
const navslide = () => {
const burger = document.querySelector(".burger");
const nav = document.querySelector(".nav-links");
const navLinks = document.querySelectorAll(".nav-links li");
nav.classList.toggle("nav-active");
navLinks.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = "";
} else {
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7 +
0.5}s`;
}
});
burger.classList.toggle("toggle");
};
return (
<div>
<nav>
<div className="logo">
<h3>College Facemash</h3>
</div>
<ul className="nav-links">
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/login">Login / Signup</Link>
</li>
</ul>
<div className="burger" onClick={navslide}>
<div className="line1"></div>
<div className="line2"></div>
<div className="line3"></div>
</div>
</nav>
</div>
);
}
}
export default navbar;
also i recommend adding state to your component and changing it when ham is clicked!
and then you can change elements classes depending on this state.
I have a hamburger nav that uses Gatsby's Link and navigates throughout the website. It works as intended, but if I'm on the same page as an anchor element that I'm clicking, the menu doesn't close. If I close it I can see that it navigated to where it needed to be.
When I add the onClick function then it overwrites the navigation, so the menu closes, but it doesn't navigate anywhere. How to solve this?
import React, { useState } from "react"
import { string } from "prop-types"
import { Link } from "gatsby"
import styles from "./styles.module.less"
const Navbar = ({ siteTitle, navColor }) => {
const [isHidden, showNavigation] = useState(true)
const links = (
<div className={styles.links}>
<Link to="/about">About</Link>
<Link to="/people">People</Link>
<Link to="/#work">Work</Link>
<Link to="/careers">Careers</Link>
<Link to="/contact-us">Contact</Link>
</div>
)
const handleMenuToggle = e => {
e.preventDefault()
showNavigation(!isHidden)
}
let nvColor = navColor ? navColor : "translate"
let logo = navColor ? blackLogo : whiteLogo
return (
<>
<header data-component="Navbar" className={styles.Navbar}>
<Link to="/" className={styles.logo} title={siteTitle}>
<img src={logo} alt={siteTitle} />
</Link>
<a
className={styles.menu}
href="#main-nav"
title="View menu"
onClick={handleMenuToggle}
style={{ color: nvColor }}
>
…
</a>
</header>
<div>
<nav id="main-nav" className={styles.MainNav} hidden={isHidden}>
<div className={styles.blocks}>
<div className={styles.LeftNav}>
<a
onClick={handleMenuToggle}
title="Hide menu"
href="#"
className={styles.close}
>
<img src={close} alt="Hide menu" />
</a>
{links}
</div>
</div>
</nav>
</div>
</>
)
}
Navbar.propTypes = {
siteTitle: string,
}
export default Navbar
You simply need to stop preventing default behavior
const handleMenuToggle = e => {
e.preventDefault() // Remove this line
showNavigation(!isHidden)
}