I am getting the error "window is not defined" in nextJS project. Here isMobile is storing the value that window size is less than 767.98 or not to execute the open/close hamburger menu functionality. This code was working fine in ReactJS but not working in NextJS. Please help me to figure out this issue.
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const isMobile = window.innerWidth <= 767.98;
const [isMenuOpen, setIsMenuOpen] = useState(!isMobile);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, []);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
Next.js is a server-side rendering framework which means the initial call to generate HTML from the server. At this point, window object, is only available on the client-side (not on the server-side).
To solve this problem, you need to check window object availability.
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const isMobile = typeof window !== "undefined" && window.innerWidth <= 767.98
const [isMenuOpen, setIsMenuOpen] = useState(!isMobile);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, []);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
Another way you can fix it is you can move that window logic into useEffect (or componentDidMount on a class-based component)
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const [isMobile, setIsMobile] = useState(false); //the initial state depends on mobile-first or desktop-first strategy
const [isMenuOpen, setIsMenuOpen] = useState(true);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
setIsMobile(window.innerWidth <= 767.98)
setIsMenuOpen(window.innerWidth > 767.98)
}, [])
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, [isMobile]);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
Note that, with this solution, your UI may have some flickering due to isMobile state
You can try this on your parent component definition.
import dynamic from 'next/dynamic'
const Navbar = dynamic(() => import('./Navbar'), { ssr: false });
const Parent = () => {
...
return (
{(typeof window !== 'undefined') &&
<Navbar/>
}
...
<Footer/>
);
}
Related
I was trying to make a profile portfolio website using react in which i made a NavBar.js for Navigation bar which i have imported in my App.js file.
But now when i was trying to run the code instead of showing the navigation bar it was showing the blank screen
NavBar.js
import { Navbar, Nav, Container } from "react-bootstrap";
import logo from '/Users/rijuljain/Documents/portfolio/portfolio-web/src/assets/img/logo.svg';
import navIcon1 from '/Users/rijuljain/Documents/portfolio/portfolio-web/src/assets/img/nav-icon1.svg';
import navIcon2 from '/Users/rijuljain/Documents/portfolio/portfolio-web/src/assets/img/nav-icon2.svg';
import navIcon3 from '/Users/rijuljain/Documents/portfolio/portfolio-web/src/assets/img/nav-icon3.svg';
import { HashLink } from 'react-router-hash-link';
import {
BrowserRouter as Router
} from "react-router-dom";
export const NavBar = () => {
const [activeLink, setActiveLink] = useState('home');
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onScroll = () => {
if (window.scrollY > 50) {
setScrolled(true);
} else {
setScrolled(false);
}
}
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, [])
const onUpdateActiveLink = (value) => {
setActiveLink(value);
}
return (
<Router>
<Navbar expand="md" className={scrolled ? "scrolled" : ""}>
<Container>
<Navbar.Brand href="/">
<img src={logo} alt="Logo" />
</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav">
<span className="navbar-toggler-icon"></span>
</Navbar.Toggle>
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ms-auto">
<Nav.Link href="#home" className={activeLink === 'home' ? 'active navbar-link' : 'navbar-link'} onClick={() => onUpdateActiveLink('home')}>Home</Nav.Link>
<Nav.Link href="#skills" className={activeLink === 'skills' ? 'active navbar-link' : 'navbar-link'} onClick={() => onUpdateActiveLink('skills')}>Skills</Nav.Link>
<Nav.Link href="#projects" className={activeLink === 'projects' ? 'active navbar-link' : 'navbar-link'} onClick={() => onUpdateActiveLink('projects')}>Projects</Nav.Link>
</Nav>
<span className="navbar-text">
<div className="social-icon">
<img src={navIcon1} alt="" />
<img src={navIcon2} alt="" />
<img src={navIcon3} alt="" />
</div>
<HashLink to='#connect'>
<button className="vvd"><span>Let’s Connect</span></button>
</HashLink>
</span>
</Navbar.Collapse>
</Container>
</Navbar>
</Router>
)
}
App.js
import 'bootstrap/dist/css/bootstrap.min.css';
import { NavBar } from "/Users/rijuljain/Documents/portfolio/portfolio-web/src/components/NavBar.js";
function App() {
return (
<div className="App">
<NavBar />
</div>
);
}
export default App;
this is the blank screen i'm getting:
plz tell what changes should i do to make it write. thank you
I'm not getting any error in my console, there it is compiling perfectly but it is not showing any output in the localhost screen
console output
enter image description here
These are the images of browser Consoleenter image description here
enter image description hereenter image description here
I have header component, where I want to toggle className between all the elements of menu (if one of the elements of menu is active and user is clicking to another element - this element become active and all others no). I have a code like this
import React, { useState } from 'react';
import './header.scss';
export const Header = ({ favoriteCount }) => {
const [activeIndex, setActiveIndex] = useState(0);
function toggleClass(index) {
setActiveIndex(index);
}
return (
<header className="header">
<div className="container header-container">
<ul className="header-menu">
<li>
<a
className={
activeIndex === 0
? 'header-menu__link active'
: 'header-menu__link'
}
onClick={() => {
toggleClass(0);
}}
href="##"
>
Characters
</a>
</li>
<li>
<a
className={
activeIndex === 1
? 'header-menu__link active'
: 'header-menu__link'
}
onClick={() => {
toggleClass(0);
}}
href="##"
>
Favorites
</a>
</li>
</ul>
<div className="header-favorite-count">
<i className="far fa-heart"></i>
{favoriteCount}
</div>
</div>
</header>
);
};
and styles to visualise toggling classes
&-menu__link {
color: lightgray;
}
.active {
color: #fff;
}
This approach is working but looks creepy. Maybe somebody knows how to optimize it?
I wouldn't use the index, I'd use the text of the item. I'd also include that text in the href so that there's an indication of what the anchor leads to. To avoid repeated code, you might put the menu items in a reusable array, something like this:
const menuItems = [
"Characters",
"Favorites",
];
export const Header = ({ favoriteCount }) => {
const [activeItem, setActiveItem] = useState("");
const setActiveItem = useCallback((event) => {
setActiveItem(event.currentTarget.href.substring(2));
}, []);
const list = menuItems.map(item =>
<li key={item}>
<a
className={`header-menu__link ${item === activeItem ? "active" : ""}`}
onClick={setActiveItem}
href={"##" + item}>
{item}
</a>
</li>
);
return (
<header className="header">
<div className="container header-container">
<ul className="header-menu">
{list}}
</ul>
<div className="header-favorite-count">
<i className="far fa-heart"></i>
{favoriteCount}
</div>
</div>
</header>
);
};
I am trying to program an alert component but I am not able to change the colour of the alert message.
It will show an alert message that dark mode is enabled when I enable it in the navbar(present at the last in the navbar component code). I am using bootstrap CSS
Alert component:
import React from "react";
export default function Alert(props) {
const capital = (word) => {
const lower = word.toLowerCase();
return lower.charAt(0).toUpperCase() + lower.slice(1);
};
return (
props.alert && (
<div
className={`alert alert-${props.alert.type} alert-dismissible fade show`}
role="alert"
>
<strong>{capital(props.alert.type)}</strong>: {props.alert.msg}
</div>
)
);
}
Navbar component:
import React from "react";
import PropTypes from "prop-types";
export default function Navbar(props) {
return (
<>
<nav
className={`navbar navbar-expand-lg navbar-${props.mode} bg-${props.mode}`}
>
<div className="container-fluid">
<a className="navbar-brand" href="/">
Navbar
</a>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav me-auto mb-2 mb-lg-0">
<li className="nav-item">
<a className="nav-link active" aria-current="page" href="/">
Home
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="/">
Link
</a>
</li>
<li className="nav-item">
<a
className="nav-link "
href="/"
tabIndex="-1"
aria-current="page"
>
{props.aboutText}
</a>
</li>
</ul>
<form className="d-flex mx-2">
<input
className="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button className="btn btn-outline-success" type="submit">
Search
</button>
</form>
<div
className={`form-check form-switch text-${
props.mode === "light" ? "dark" : "light"
} mx-2`}
>
<input
className="form-check-input "
onClick={props.togglemode}
type="checkbox"
id="flexSwitchCheckDefault"
/>
<label
className={`form-check-label `}
htmlFor="flexSwitchCheckDefault"
>
Enable Dark Mode
</label>
</div>
</div>
</div>
</nav>
</>
);
}
App.js:
import "./App.css";
import Navbar from "./components/Navbar";
import React, { useState } from "react";
import Alert from "./components/Alert";
function App() {
const [mode, setMode] = useState("light");
const [alert, setAlert] = useState(null);
const showAlert = (message, type) => {
setAlert({
msg: message,
type: type,
});
setTimeout(() => {
setAlert(null);
}, 1500);
};
const togglemode = () => {
if (mode === "light") {
setMode("dark");
document.body.style.backgroundColor = "#042743";
showAlert("Dark mode has been enabled", "Success");
} else {
setMode("light");
document.body.style.backgroundColor = "white";
showAlert("Light mode has been enabled", "Success");
}
};
return (
<>
<Navbar aboutText="About Myself" mode={mode} togglemode={togglemode} />
<div className="container " my-3="true">
<Alert alert={alert} />
</div>
</>
);
}
export default App;
change the showAlert functions 2nd property:
from "Success" to "success". it will work.
To make it easier for you, replace:
showAlert("Dark mode has been enabled", "Success");
with:
showAlert("Dark mode has been enabled", "success");
at both places in the togglemode function.
I am working on my first react project and I got a bit stuck.
I am trying to create a navigation menu where when you click, for example, "men", a dropdown appears.
The issue I am having is that there shall only be one of these dropdowns showing at the same time, I have a total of 6 dropdowns.
I have been trying to remove the class from the elements and then re-adding it, but the issue is that it toggles the class back.
Here is the code:
const BottomNavigation = (props) => {
const [menShowing, setMenShowing] = useState(false);
const [womenShowing, setWomenShowing] = useState(false);
const [accessoriesShowing, setAccessoriesShowing] = useState(false);
const [shoesShowing, setShoesShowing] = useState(false);
const [faceBodyShowing, setFaceBodyShowing] = useState(false);
const [newInShowing, setNewInShowing] = useState(false);
const menHandler = () => setMenShowing((prevCheck) => !prevCheck);
const womenHandler = () => setWomenShowing((prevCheck) => !prevCheck);
const accessoriesHandler = () =>
setAccessoriesShowing((prevCheck) => !prevCheck);
const shoesHandler = () => setShoesShowing((prevCheck) => !prevCheck);
const faceBodyHandler = () => setFaceBodyShowing((prevCheck) => !prevCheck);
const newInHandler = () => setNewInShowing((prevCheck) => !prevCheck);
return (
<nav>
<div className={classes.bottom_navigation}>
<div className={classes.nav_items_container}>
<ul className={classes.nav_link}>
<li className={`${classes.nav_item}`}>
<p onClick={menHandler}>men</p>
<div
key="men"
className={`${classes.nav_dropdown} ${classes.dropdown__men} ${
menShowing ? classes.dropdown_show : null
}`}
>
<Men />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={womenHandler}>women</p>
<div
key="women"
className={`${classes.nav_dropdown} ${
classes.dropdown__women
} ${womenShowing ? classes.dropdown_show : null} `}
>
<Women />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={accessoriesHandler}>accessories</p>
<div
key="accessories"
className={`${classes.nav_dropdown} ${
classes.dropdown__accessories
} ${accessoriesShowing ? classes.dropdown_show : null} `}
>
<Accessories />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={shoesHandler}>shoes</p>
<div
key="shoes"
className={`${classes.nav_dropdown} ${
classes.dropdown__shoes
} ${shoesShowing ? classes.dropdown_show : null} `}
>
<Shoes />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={faceBodyHandler}>face + body</p>
<div
key="facebody"
className={`${classes.nav_dropdown} ${
classes.dropdown__facebody
} ${faceBodyShowing ? classes.dropdown_show : null} `}
>
<FaceBody />
</div>
</li>
<li className={classes.nav_item}>
<p onClick={newInHandler}>new in</p>
<div
key="newin"
className={`${classes.nav_dropdown} ${
classes.dropdown__newin
} ${newInShowing ? classes.dropdown_show : null} `}
>
<NewIn />
</div>
</li>
</ul>
</div>
<div className={classes.options_container}>
<div className={classes.icon_container}>
<span className={classes.cart_sum}>$0.00</span>
<Cart className={classes.icon} />
</div>
<div className={classes.icon_container}>
<Heart className={classes.icon} />
</div>
<div className={classes.icon_container}>
<Visibility className={classes.icon} />
</div>
<div className={classes.icon_container}>
<User className={classes.icon} />
</div>
</div>
</div>
</nav>
);
};
If any other component is needed to solve this issue, let me know!
Instead of keeping every item's showing state in different useState's, you can use a single useState and a single menu click handler like:
const [showItem, setShowItem] = useState(null);
const menuClickHandler = (param) => {
if (showItem === param) {
setShowItem(null);
} else {
setShowItem(param);
}
};
and in your nav items you can call menu click handler and you can check if the current state is the name of current item like:
<li className={`${classes.nav_item}`}>
<p onClick={() => menuClickHandler("men")}>men</p>
<div
key="men"
className={`${classes.nav_dropdown} ${classes.dropdown__men} ${
showItem === "men" ? classes.dropdown_show : null
}`}
>
<Men />
</div>
</li>
You can take a look at this sandbox for this usage.
I highly recommend solving this by making a new component that you can reuse. I'll call mine Dropdown. This takes advantage of React's use of state. We want each dropdown to control it's own state of being open or not. Changing classes works, but I don't think you're properly leveraging what React can do.
Here's my quick version of a Dropdown, which you can test out here. The tech I use here is mostly React Hooks.
import "./dropdown.css";
import React, { useCallback, useEffect, useRef, useState } from "react";
const Dropdown = ({ onOpen, label, children }) => {
const [isOpen, setIsOpen] = useState(false);
const containerRef = useRef();
const onMouseDown = useCallback(
(e) => {
if (containerRef.current && !containerRef.current.contains(e.target)) {
setIsOpen(false);
}
},
[containerRef, setIsOpen]
);
useEffect(() => {
if (isOpen) {
onOpen();
}
}, [isOpen, onOpen]);
useEffect(() => {
window.addEventListener("mousedown", onMouseDown);
return () => window.removeEventListener("mousedown", onMouseDown);
}, [onMouseDown]);
const toggleIsOpen = () => {
setIsOpen(!isOpen);
}
return (
<div ref={containerRef} className="dropdown-container">
<div className="dropdown-label" onClick={toggleIsOpen}>{label}</div>
{isOpen && <div className="dropdown-content">{children}</div>}
</div>
);
};
export default Dropdown;
Here it is in action inside of a simple App.js
import React from "react";
import Dropdown from "./Dropdown.js";
function App() {
return (
<div className="App">
<div className="menu">
<Dropdown
label={<div>Men</div>}
onOpen={() => console.log("Opening men")}
>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
</Dropdown>
<Dropdown
label={<div>Women</div>}
onOpen={() => console.log("Opening women")}
>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
</Dropdown>
<Dropdown
label={<div>Shoes</div>}
onOpen={() => console.log("Opening shoes")}
>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
</Dropdown>
<Dropdown
label={<div>Accessories</div>}
onOpen={() => console.log("Opening accessories")}
>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
<div>Test Menu</div>
</Dropdown>
</div>
</div>
);
}
export default App;
Let me know if you have questions!
I trying to find a way to open the navbar of ReactJS app when i'm clicking on my "MENU" button.
At the beginning my nav component have a width of 0px (with overflow : hidden). When i'm clicking on the button my nav should have a width of 400px. I'm a beginner with React.
I have two React Components :
Topbar
export default function Topbar() {
return (
<div className="topbar__container">
<div className='topbar__menuButton'>
<Link className="topbar__link">MENU</Link>
</div>
<div className="topbar__title">
<Link to="/" className="topbar__link">EDGAR</Link>
</div>
</div>
)
}
Nav
const Nav = () => {
return (
<div className="navbar__container">
<Query query={CATEGORIES_QUERY} id={null}>
{({ data: { categories } }) => {
return (
<nav className="nav">
<ul>
{categories.map((category, i) => {
return (
<li key={category.id}>
<Link to={`/category/${category.id}`} className="nav__link">
{category.name}
</Link>
</li>
)
})}
</ul>
</nav>
)
}}
</Query>
</div>
)
}
export default Nav
To achieve something like that you have to set this logic in the common parent of both component (here App for the example).
App will manage a state to determine if the Nav is open or not. The state is called isMenuOpen and can be changed using the setIsMenuOpen() function. We will give to the children Nav the state isMenuOpen and to the children TopBar a callback from the function setIsMenuOpen():
App.jsx
import React from "react";
import TopBar from "./TopBar";
import Nav from "./Nav";
export default function App() {
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
return (
<div className="App">
<TopBar setMenuStatus={setIsMenuOpen} />
<Nav isOpen={isMenuOpen} />
</div>
);
}
Then the TopBar have to set the value of isMenuOpen to true using the function setIsMenuOpen() from the props.
TopBar.jsx
import React from "react";
export default function Topbar({ setMenuStatus }) {
return (
<div className="topbar__container">
<div className="topbar__menuButton">
<button
type="button"
onClick={() => {
setMenuStatus(true);
}}
>
Menu
</button>
</div>
</div>
);
}
Then the component Nav will set a specific class (here .open) if isOpen coming from props is true.
Nav.jsx
import React from "react";
import "./styles.css";
export default function Nav({ isOpen }) {
return (
<div id="nav" className={isOpen ? "open" : ""}>
Menu
</div>
);
}
styles.css
#nav {
display: none;
}
#nav.open {
height: 400px;
display: inline-block;
}
You can try this example in this codesandbox.
import React, {useState} from "react";
import "./styles.css";
export default function App() {
const [toggle, setToggle]= React.useState(false)
const [width, setWidth]= React.useState('')
const showMenu = () => {
setToggle(!toggle)
if(toggle === true) {
setWidth('50px')
}else {
setWidth('500px')
}
}
return (
<div className="App">
<button onClick={showMenu}>Menu</button>
<div style={{width, border:'1px solid red'}}>
<li>text</li>
<li>text</li>
<li>text</li>
<li>text</li>
</div>
</div>
);
}
reproducing link: https://codesandbox.io/s/billowing-flower-rxdk3?file=/src/App.js:0-592