I am following a tutorial that creates an animated navbar and hamburger manu using css, html and javascript, and I want to create the navbar as a react component for my project.
The code that I am trying to covert is the following, which is an app.js file which is embedded in the html with tags:
const navSlide = () => {
const burger = document.querySelector('.burger');
const nav = document.querySelector('.nav-links');
burger.addEventListener('click', () => {
nav.classList.toggle('nav-active');
})
}
navSlide();
I have tried to implement this code into my navbar react component as follows:
import React from "react";
const NavBar = () => {
const nav = document.querySelector(".nav-links");
return (
<div className="navbar">
<nav>
<ul className="nav-links">
<li>
Menu Item
</li>
</ul>
<div
className="burger"
onClick={() => {
nav.classList.toggle("nav-active");
}}
>
<div className="line1" />
<div className="line2" />
<div className="line3" />
</div>
</nav>
</div>
);
};
export default NavBar;
However this returns the error of:
"TypeError: Cannot read property 'classList' of null"
The CSS of .nav-active is as follows:
.nav-active {
transform: translateX(0%);
}
The result of this I am expecting is for the navbar to toggle open and closed when the burger symbol is clicked
The best option is to go with state. This is should work, have not tested though. ReactJS 16.8 is required for useState hook to work.
import React, {useState} from "react";
const NavBar = () => {
const [navOpened, setNavOpened] = useState(false);
const navClassNames = navOpened ? 'nav-links nav-active' : 'nav-links';
return (
<div className="navbar">
<nav>
<ul className={navClassNames}>
<li>
Menu Item
</li>
</ul>
<div
className="burger"
onClick={() => setNavOpened(!navOpened)}
>
<div className="line1" />
<div className="line2" />
<div className="line3" />
</div>
</nav>
</div>
);
};
To refer to DOM elements you should be using ref, you can use a useRef hook docs.
import React, { useRef } from "react";
const NavBar = () => {
const nav = useRef(null); // initial value of ref null
return (
<div className="navbar">
<nav>
<ul className="nav-links" ref={nav}> {/* Put the ref in your JSX */}
<li>
Menu Item
</li>
</ul>
<div
className="burger"
onClick={() => {
nav.current.classList.toggle("nav-active"); // Now you can use the ref here, current is required to get the current value of the ref
}}
>
<div className="line1" />
<div className="line2" />
<div className="line3" />
</div>
</nav>
</div>
);
};
export default NavBar;
Related
Guys I need your help with an issue. I am recently learning how to use React. The question is this: I have three h2 elements, each with a different id attribute. What I am trying to do is create a list consisting of as many "a" elements as there are h2 elements. Each "a" element should have the href attribute pointing to the respective h2 element.
I show you what I have tried to do. With the code I wrote the list remains blank.
import logo from './logo.svg';
import './App.css';
import { Link } from "react-router-dom";
function App() {
const h2 = Array.from(document.getElementsByTagName('h2'));
const list = h2.map(element => {
return (
<li key={element.id}>
<Link to={`${element.id}`}>{element.innerHTML}</Link>
</li>
);
})
return (
<div className="App">
<h2 id="first">First item</h2>
<h2 id="second">Second item</h2>
<h2 id="Third">Third item</h2>
<h4>List of h2:</h4>
<ul>
{list}
</ul>
</div>
);
}
export default App;
Could you tell me what I should do or the concepts I should go to study?
There are two problems in above solution.
Fetching H2s before they render for the first render.
Missed appending # before redirection link for ID.
Here's the solution. I have used a tag instead of react-router's Link but it will work in similar pattern.
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [list, setList] = useState([]);
useEffect(() => {
// Added this in useEffect as we endup fetching h2s before they render if kept outside. Here they will be fetched post first render
const h2 = Array.from(document.getElementsByTagName("h2"));
const currentList = h2.map((element) => {
return (
<li key={element.id}>
<a href={`#${element.id}`}>{element.innerHTML}</a>
</li>
);
});
setList(currentList);
}, []);
return (
<div className="App">
<h2 id="first">First item</h2>
<h2 id="second">Second item</h2>
<h2 id="Third">Third item</h2>
<div style={{ height: "100vh" }}>Some space</div>
<h4>List of h2:</h4>
<ul>{list}</ul>
</div>
);
}
Here's a working example at codesandbox, I have added a large div in the center to make scroll visible. Please scroll down the example.
Using useEffect to store the text of h2's and creating Links from it as as below,
change the li to Links as required
const { useState, useEffect } = React;
function App() {
const [h2Count, setH2Count] = useState([]);
useEffect(() => {
const noOfH2s = [...document.querySelectorAll("h2")];
setH2Count(noOfH2s.map((el) => ({ text: el.innerText, id: el.id })));
}, []);
const list = h2Count.map((element, id) => {
return <li key={id}>{`${element.text}-${element.id}`}</li>;
});
return (
<div className="App">
<h2 id="first">First item</h2>
<h2 id="second">Second item</h2>
<h2 id="Third">Third item</h2>
<h4>List of h2:</h4>
<ul>{list}</ul>
</div>
);
}
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
Please help, I have been trying to write the following code in react syntax but I don't seem to know how to do that and all search I've made online isn't helping
useEffect(() => {
const menuBtn = document.getElementById('menu-btn');
const menu = document.querySelector('.menu')
menuBtn.addEventListener('click', () =>{
menu.classList.toggle('menu-open')
})
}, []);
useEffect(()=>{
const navbar = document.getElementById('navbar')
const offset = 50
window.addEventListener("scroll", () =>{
if(pageYOffset > offset){
navbar.classList.add('navbar-active')
}else{
navbar.classList.remove('navbar-active')
}
})
}, [])
I want to change it because I keep getting the error below
[webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'Compilation/modules|/home/kimmoramicky/Desktop/fts_portfolio/node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[2].oneOf[7].use[1]!/home/kimmoramicky/Desktop/fts_portfolio/node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[2].oneOf[7].use[2]!/home/kimmoramicky/Desktop/fts_portfolio/node_modules/bootstrap/dist/css/bootstrap.min.css': No serializer registered for Warning
while serializing webpack/lib/cache/PackFileCacheStrategy.PackContentItems -> webpack/lib/NormalModule -> Array { 1 items } -> webpack/lib/ModuleWarning -> Warning
but when I comment that part out, I don't get the error. Also, the code doesn't work as expected when the page is loaded again.
Below is the complete code for clearer understanding
import React, {useState, useEffect, useRef} from 'react'
import Link from 'next/link'
import { getPosts, getTags, getCategories } from '../services';
import { useRouter } from 'next/router';
import 'react-ionicons'
import 'react-ionicons'
import { FaFacebookSquare, FaGithubSquare, FaInstagramSquare, FaYoutubeSquare } from 'react-icons/fa'
import {ImMenu} from 'react-icons/im'
import {BiSearchAlt} from 'react-icons/bi'
import Button from 'react-bootstrap/Button';
import Container from 'react-bootstrap/Container';
import Form from 'react-bootstrap/Form';
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import NavDropdown from 'react-bootstrap/NavDropdown';
import { filter } from 'domutils';
const Header = () => {
useEffect(() => {
const menuBtn = document.getElementById('menu-btn');
const menu = document.querySelector('.menu')
menuBtn.addEventListener('click', () =>{
menu.classList.toggle('menu-open')
})
}, []);
useEffect(()=>{
const navbar = document.getElementById('navbar')
const offset = 50
window.addEventListener("scroll", () =>{
if(pageYOffset > offset){
navbar.classList.add('navbar-active')
}else{
navbar.classList.remove('navbar-active')
}
})
}, [])
return (
<div className='header1'>
<video id='headerVideo' loop={true} autoplay="autoplay" muted>
<source src="/vidfy-african-cheerful-young-stylish-man-and-w_1920x1080.mp4" type="video/mp4"></source>
Your browser does not support the video tag.
</video>
<div>
<nav id='navbar'>
<div className="menu">
<div>
<a href="#" className='brand'>Menu</a>
<ul>
<li>Blog</li>
<li>What's New</li>
<li>Categories</li>
</ul>
<ul>
<li>Projects</li>
<li>News</li>
<li>Store</li>
<li>About</li>
<li>Contact</li>
</ul>
<ul className='social-media'>
<li><a href="#">
<FaFacebookSquare />
</a></li>
<li><a href="#">
<FaInstagramSquare />
</a></li>
<li><a href="#">
<FaGithubSquare />
</a></li>
<li><a href="#">
<FaYoutubeSquare />
</a></li>
</ul>
<form action="">
<div className="input-wrap">
<input type="search" placeholder='Search...' />
<button type='submit'>
<BiSearchAlt />
</button>
</div>
</form>
</div>
</div>
<div className="container">
<img src="/logo.png" alt="logo" />
<div className="container-inner">
<ul>
<li>Home</li>
<li>Home</li>
<li>Home</li>
</ul>
<form action="">
<div className="input-wrap">
<input type="search" placeholder='Search...' className='text-is-white' />
<button type='submit'>
<BiSearchAlt />
</button>
</div>
</form>
</div>
<ImMenu className='fa-bars' id='menu-btn' />
</div>
</nav>
</div>
<div className="headText">
<p>Grow your business and ideas with KimmoTech</p>
</div>
</div>
)
}
export default Header
so you have a bunch of stuff you should not do that way:
First:
const menuBtn = document.getElementById('menu-btn');
Should be:
export const Component = () => {
const [menuOpen, setMenuOpen = useState(false)
const handleMenuButtonClick = () => setMenuOpen(!menuOpen)
return (
…
<div className={‘menu ${menuOpen ? ‘menu-open’ : ‘menu-closed’}’}/>
<button onClick={handleMenuButtonClick} />
…
)
Then this:
window.addEventListener("scroll", () =>{
if(pageYOffset > offset){
navbar.classList.add('navbar-active')
}else{
navbar.classList.remove('navbar-active')
}
})
}, [])
This is better to use as hook and when you do that you have two things you should have in mind, first one this should be DEBOUNCED otherwise you’l have quite nasty perf hit, second when you subscribe to event like that you have to also unsubscribe:
cons [scrollActive, setScrollActive] = useState(false)
useEffect(() => {
const scroll = window.addEventListener(‘scroll’, (e) => setScrollActive(e > condition));
return () => winodw.removeEventListener(scroll)
}, [])
…
<navbar classNsme={‘navbar ${scrollActive ? ‘scroll-active’ : ‘’}}/>
They would look more like react then it is right now…
The issue occurred because you used webpack on react app.
It'll be disappeared if you create the app with "create-react-app".
Hope it'll be helpful for you.
I would recommend to switch from useEffect to using useState to manage the state of your components including classes. Also React provides an easy and smooth way to manage your event handlers such as onClick.
Here is an example of how I would approach your problem, it's not going to be your code exactly but hopefully you can replicate the approach to work with you code:
import React, { useState } from 'react';
const Example = () => {
const [classes, setClasses] = useState('class-1');
const handleToggleClasses = () => {
setClasses(classes === 'class-1' ? 'class-2' : 'class-1')
}
return (
<>
<div className={classes}>
my classes will change when the button is clicked
</div>
<button onClick={handleToggleClasses}>
click me to toggle classes
</button>
</>
)
}
This button in this code has a an onClick handler which is equivalent to assigning it an event listener using vanilla JS like you did in your useEffect and I pass to it the handleToggleClasses function which just toggles between the classes using useState to keep track of that. It's cleaner, more performant and concise.
you can read more about handling events here
I hope that helps!
On the small screen, it doesn't show/hide. How can I change this code to work on the small screen?
import React from 'react';
import assets from './assets';
import { FaBars, FaFacebook, FaRegHeart, FaInstagram, FaLinkedin, FaStar, FaStarHalf, FaTimes, FaTwitter } from 'react-icons/fa';
var navLinks = document.getElementById('navLinks');
function showMenu() {
navLinks.style.right = "0";
}
function hideMenu() {
navLinks.style.right = "-200px";
}
Above var worked a few times mistakenly, but I still did not get it.
export const Home = () => (
<>
<section class="header">
<nav>
<img src={assets.logo} alt='Logo' width='100%' />
<div id='navLinks' class='nav-links'>
<FaTimes class='fa' onClick={showMenu} />
<ul>
<li>HOME</li>
<li>ABOUT</li>
<li>COURSE</li>
<li>BLOG</li>
<li>CONTACT</li>
</ul>
</div>
<FaBars class='fa' onClick={hideMenu} />
</nav>
<div class="text-box">
<h1>World's Biggest University</h1>
<p>Making website is now one of the easiest thing in the world. You just need to learn HTML, CSS,<br />Javascript and you are good to go.</p>
Visit Us to Know More
</div>
</section>
</>
)
My CSS file is below.
I've tried many ways but I couldn't.
nav .fa{
display: none;
}
As some people have mentioned before you are not utilising the React library correctly.
The (untested) code below should suit you better. As you can see a state has been used instead of directly changing style properties. I have tried to comment the code as clearly as possible but for more in depth instructions i recommend reading the React docs.
import React, { useState } from 'react';
import assets from './assets';
import { FaBars, FaFacebook, FaRegHeart, FaInstagram, FaLinkedin, FaStar, FaStarHalf, FaTimes, FaTwitter } from 'react-icons/fa';
export const Home = () => {
// Set state value to keep track of menu open or closed status
const [navOpen, setNavOpen] = useState(false)
// Open the active menu
const open = () => {
setNavOpen(true)
}
// Close the active menu
const close = () => {
setNavOpen(false)
}
return (
<>
<section class="header">
<nav>
<img src={assets.logo} alt='Logo' width='100%' />
<div id='navLinks' class='nav-links'>
<button onClick={open}>
<FaTimes class='fa' />
</button>
{
// Only render the UL node when the navOpen variable is true
navOpen && (
<ul>
<li>HOME</li>
<li>ABOUT</li>
<li>COURSE</li>
<li>BLOG</li>
<li>CONTACT</li>
</ul>
)
}
</div>
<button onClick={close}>
<FaBars class='fa' />
</button>
</nav>
<div class="text-box">
<h1>World's Biggest University</h1>
<p>Making website is now one of the easiest thing in the world. You just need to learn HTML, CSS,<br />Javascript and you are good to go.</p>
Visit Us to Know More
</div>
</section>
</>
)
}
i take my first steps on react.
I'm working on a gatsbyjs site, and i'm stuck on my nav component.
Following some tuts, I started creating a class component called Burger to show/hide the nav... setting props, state and bind.. no probs right now.
On another file i put my Nav functional components, using gatsbyjs "Link" component to manage links.
What i'm trying to do, is to change the state of the "Burger" component when I click on the link.
So I tried using the onClick event:
onClick={() => props.handleClick()}
it doesn't work and the error is: props.handleClick is not a function.
What im doing wrong?
thanks
// Burger.js
class Burger extends React.Component {
constructor(props) {
super(props);
this.state = { isOpen: false };
this.handleClick = this.handleClick.bind(this);
}
handleClick = () => {
this.setState({
isOpen: !this.state.isOpen,
});
this.state.isOpen
? document.body.classList.remove("nav-open")
: document.body.classList.add("nav-open");
};
render() {
const burgerState = this.state.isOpen ? open : "";
return (
<button className={`${burger} ${burgerState}`} onClick={this.handleClick}>
<span className={burger__label}>Menu</span>
<span className={burger__line}></span>
<span className={burger__line}></span>
<span className={burger__line}></span>
</button>
);
}
}
// Nav.js
const Nav = (props) => {
return (
<div className={nav__overlay}>
<div className={nav__wrap}>
<nav className={nav__primary}>
<ul>
<li>
<Link to='/' activeClassName={active} title='Home' onClick={() => props.handleClick()}>
Home
</Link>
</li>
<li>
<Link to='/about' activeClassName={active} title='About' onClick={() => props.handleClick()}>
About
</Link>
</li>
<li>
<Link to='/contact' activeClassName={active} title='Contact' onClick={() => props.handleClick()}>
Contact
</Link>
</li>
<li>
<Link to='/blog' activeClassName={active} title='Blog' onClick={() => props.handleClick()}>
Blog
</Link>
</li>
</ul>
</nav>
<div className='contact'>
<ul>
<li>aaa</li>
<li>aaa</li>
<li>aaa</li>
<li>aaa</li>
</ul>
</div>
</div>
</div>
);
};
Is the error with the Burger component or the Nav component? It's not clear how Nav.js or Burger.js are related or interact with one another. Another thing is that React doesn't care if the nav-open class is on the body or not, so if hooked up properly the function should be callable.
Given the error you provided — onClick={() => props.handleClick()} — it looks like this code is in the Nav component. Wherever you render the nav, you need to pass handleClick as a prop, e.g. <Nav handleClick={myFunction} />.
You can read more about props and how they work here: https://reactjs.org/docs/components-and-props.html
I am getting this error while I use state.clicked statement inside if-else condition.
Below is my code,
Hamburger.js
import React, { useEffect, useRef } from "react";
import { Link } from "react-router-dom";
const Hamburger = ({ state }) => {
let menu = useRef(null);
useEffect(() => {
if (state.clicked === false) {
//close our menu
menu.style.display = "none";
} else if (
state.clicked === true ||
(state.clicked === true && state.initial === null)
) {
// open our menu
menu.style.display = "block";
}
});
return (
<div ref={(el) => (menu = el)} className="hamburger-menu">
<div className="menu-secondary-background-color"></div>
<div className="menu-layer">
<div className="container">
<div className="wrapper">
<div className="menu-links">
<nav>
<ul>
<li>
<Link to="/opportunities">Opportunities</Link>
</li>
<li>
<Link to="/opportunities">Work</Link>
</li>
<li>
<Link to="/opportunities">Contact</Link>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
);
};
export default Hamburger;
Header.js
import React, { useState } from "react";
import { Link } from "react-router-dom";
import Hamburger from "./Hamburger";
const Header = () => {
const [state, setState] = useState({
initial: false,
clicked: null,
menuName: "Menu",
});
const [disabled, setDisabled] = useState(false);
const handleMenu = () => {
disableMenu();
if (state.initial === false) {
setState({
inital: null,
clicked: true,
menuName: "Close",
});
} else if (state.clicked === true) {
setState({
clicked: !state.clicked,
menuName: "Menu",
});
} else if (state.clicked === false) {
setState({
clicked: !state.clicked,
menuName: "Close",
});
}
};
// Determine if our menu button should be disabled
const disableMenu = () => {
setDisabled(!disabled);
setTimeout(() => {
setDisabled(false);
}, 1200);
};
return (
<header>
<div className="container">
<div className="wrapper">
<div className="inner-header">
<div className="logo">
<Link to="/">Adarsh Goldar</Link>
</div>
<div className="menu">
<Link to="/">Home</Link>
<Link to="/">About</Link>
<button disabled={disabled} onClick={handleMenu}>
Menu
</button>
</div>
</div>
</div>
</div>
<Hamburger />
</header>
);
};
export default Header;
Yes because you have not passed state as a prop to do that change your code as below
in header.js
<Hamburger state={state}/>
well you need to pass state props to Your component:
<Hamburger state={state}/>
and for Hamburger you dont need to use useEffect and ref to update the style because when state props change the component will re render and every code will run again.
here how to do it with jsx style:
const Hamburger = ({ state }) => {
return (
<div style={{display:state.clicked?"block":"none"}} className="hamburger-menu">
<div className="menu-secondary-background-color"></div>
<div className="menu-layer">
<div className="container">
<div className="wrapper">
<div className="menu-links">
<nav>
<ul>
<li>
<Link to="/opportunities">Opportunities</Link>
</li>
<li>
<Link to="/opportunities">Work</Link>
</li>
<li>
<Link to="/opportunities">Contact</Link>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
);
};
export default Hamburger;
and you can wrap Hamburger component in React.memo to prevent unnecessary re render so it will re render only when state props change : export default React.memo(Hamburger)`