How to change the active class in a navbar in react - reactjs

Problem:
I am creating a React web application. In there I have created a side navbar like this.
import React, { PureComponent } from "react";
import imagine from "../../../assets/img/sidebar-2.jpg";
class Sidebar extends PureComponent {
constructor(props) {
super(props);
this.state = {
width: window.innerWidth,
activeTabClassName: "tab1"
};
}
// activeRoute(routeName) {
// return this.props.location.pathname.indexOf(routeName) > -1 ? "active" : "";
// }
updateDimensions() {
this.setState({ width: window.innerWidth });
}
componentDidMount() {
this.updateDimensions();
window.addEventListener("resize", this.updateDimensions.bind(this));
}
render() {
const sidebarBackground = {
backgroundImage: "url(" + imagine + ")"
};
return (
<div className="sidebar" data-image={imagine} id="sidebar">
<div className="sidebar-background" style={sidebarBackground} />
<div className="sidebar-wrapper">
<div className="logo">
<a href="/" className="simple-text">
<img
src="../../images/favicon.png"
style={{ maxHeight: "50px" }}
/>
Trafficfine
</a>
</div>
<ul className="nav">
<li className="nav-item active">
<a className="nav-link" href="/admin/dashbord">
<i className="fas fa-tachometer-alt" />
<p>Dashboard</p>
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="/admin/officers">
<i className="fas fa-briefcase" />
<p>Officers</p>
</a>
</li>
<li>
<a className="nav-link" href="/admin/offences">
<i className="fas fa-times-circle" />
<p>Offences</p>
</a>
</li>
<li>
<a className="nav-link" href="/admin/drivers">
<i className="fas fa-bus-alt" />
<p>Drivers</p>
</a>
</li>
</ul>
</div>
</div>
);
}
}
export default Sidebar;
I want to change the active class dynamically when a user is clicking on the nav Item and add some styling to the active class. I saw some similar questions have been asked on the stack overflow and I tried those example. But through those, I was unable to achieve that. Can someone help me to solve my problem by modifying
my code? Thank you.

You already have your target class in state, you just need to add it like this
<div className={"sidebar " + this.state.activeTabClassName} data-image={imagine} id="sidebar">
And call this.setState( {activeTabClassName: "newActiveClass"} ) when you want to change it.

Related

The Profile Picture is not visible on the Top Bar

This a blog page using the Fullstack MERN Application. And it should show the uploaded Profile pic of the user when the particular user is Logged in on the Top bar. But in my case it is not showing up. Same thing is happening for the About me Image. But the difference is that the Image in the About me component is visible when the user is logging out and then logging in again.
The code for the Top Bar component is :
import { useContext } from "react";
import { Link } from "react-router-dom";
import { Context } from "../../context/Context";
import "./topbar.css";
export default function TopBar() {
const { user, dispatch } = useContext(Context);
const PF = "http://localhost:5000/images"
// console.log(PF+user.profilePic)
const handleLogout = () => {
dispatch({ type: "LOGOUT" });
};
return (
<div className="top">
<div className="topLeft">
<i className="topIcon fab fa-facebook-square"></i>
<i className="topIcon fab fa-twitter-square"></i>
<i className="topIcon fab fa-pinterest-square"></i>
<i className="topIcon fab fa-instagram-square"></i>
</div>
<div className="topCenter">
<ul className="topList">
<li className="topListItem">
<Link className="link" to="/">
HOME
</Link>
</li>
<li className="topListItem">
<Link className="link" to="/">
ABOUT
</Link>
</li>
<li className="topListItem">
<Link className="link" to="/">
CONTACT
</Link>
</li>
<li className="topListItem">
<Link className="link" to="/write">
WRITE
</Link>
</li>
<li className="topListItem" onClick={handleLogout}>
{user && "LOGOUT"}
</li>
</ul>
</div>
<div className="topRight">
{user ? (
<Link to="/settings">
<img className="topImg" src={PF + user.profilePic} alt="" />
</Link>
) : (
<ul className="topList">
<li className="topListItem">
<Link className="link" to="/login">
LOGIN
</Link>
</li>
<li className="topListItem">
<Link className="link" to="/register">
REGISTER
</Link>
</li>
</ul>
)}
<i className="topSearchIcon fas fa-search"></i>
</div>
</div>
);
}
COde For the Sidebar:
import axios from "axios";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import "./sidebar.css";
import { useContext } from "react";
import { Context } from "../../context/Context";
export default function Sidebar() {
const [cats, setCats] = useState([]);
const { user } = useContext(Context);
const PF = "http://localhost:5000/images/";
useEffect(() => {
const getCats = async () => {
const res = await axios.get("/categories");
setCats(res.data);
};
getCats();
}, []);
return (
<div className="sidebar">
<div className="sidebarItem">
<span className="sidebarTitle">ABOUT ME</span>
{user && <img
src={PF + user.profilePic}
alt=""
/>}
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptate qui
necessitatibus nostrum illum reprehenderit.
</p>
</div>
<div className="sidebarItem">
<span className="sidebarTitle">CATEGORIES</span>
<ul className="sidebarList">
{cats.map((c) => (
<Link to={`/?cat=${c.name}`} className="link">
<li className="sidebarListItem">{c.name}</li>
</Link>
))}
</ul>
</div>
<div className="sidebarItem">
<span className="sidebarTitle">FOLLOW US</span>
<div className="sidebarSocial">
<i className="sidebarIcon fab fa-facebook-square"></i>
<i className="sidebarIcon fab fa-twitter-square"></i>
<i className="sidebarIcon fab fa-pinterest-square"></i>
<i className="sidebarIcon fab fa-instagram-square"></i>
</div>
</div>
</div>
);
}
The Image of the Top Bar
Also when the user is uploading the image a random no. is added in front of the file in the Data base which
I have manually remove to load the image to the server. As it is not able to read it like:
The random numbers in frond of the name of the file
I tried some Stack over flow answer like:TypeError: Cannot read property 'image' of null
This helped me with the Side bar problem but not fully.

NextJS and bootstrap mobile nav

I'm using bootstrap with NextJS for my navbar but have one issue because of the dynamic page changes with React and Next on mobile view the menu stays open after clicking a link and loading new page.
Tried a few ways to try and make it close on page changes but been unsuccessful. What would be best way to achieve this?
import { useEffect } from 'react'
import Link from 'next/link'
import Image from 'next/image'
import { ArrowDownCircle } from 'react-bootstrap-icons'
import logo from '../public/images/logo.gif'
import styles from '../styles/Navbar.module.scss'
const Navbar = () => {
useEffect(() => {
import('bootstrap/js/dist/collapse')
import('bootstrap/js/dist/dropdown')
}, []);
return (
<nav id="navbar" className="navbar navbar-expand-md navbar-light container">
<div>
<Link href="/" prefetch={false}><a className="navbar-brand">
<Image
src={logo}
alt="Logo"
layout="intrinsic"
width={200}
height={50}
priority={true}
/>
</a></Link>
</div>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className={`${styles.navLink} navbar-nav ms-auto`}>
<li className="nav-item">
<Link href="/" prefetch={false}><a className={`${styles.link} nav-link`} aria-current="page">Home</a></Link>
</li>
<li className="nav-item">
<Link href="/about" prefetch={false}><a className={`${styles.link} nav-link`}>About</a></Link>
</li>
<li className="nav-item dropdown">
<a className={`${styles.link} nav-link`} id="navbarDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false">Services <ArrowDownCircle className="ms-1" /></a>
<ul className="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<li><Link href="/web-design"><a className="dropdown-item">Web Design</a></Link></li>
<li><Link href="/web-development"><a className="dropdown-item">Web Development</a></Link></li>
<li><Link href="/ecommerce"><a className="dropdown-item">eCommerce</a></Link></li>
</ul>
</li>
<li className="nav-item">
<Link href="/faq" prefetch={false}><a className={`${styles.link} nav-link`}>FAQ</a></Link>
</li>
<li className="nav-item">
<Link href="/contact" prefetch={false}><a className={`${styles.link} nav-link`}>Contact</a></Link>
</li>
</ul>
</div>
<style jsx>{`
a {
background: none;
}
a:hover {
background: none;
}
`}</style>
</nav>
);
}
export default Navbar;
If anyone still looking for solution in 2022 or later, follow these steps:
Note: This solution was tested on NextJs (Typescript) + Bootstrap v.5.2
Add an id to the button with class navbar-toggler i.e. <button id="navbar-toggler" className="navbar-toggler" ...
In your component class, say Header.tsx
Import useRouter and useEffect:
import { useRouter } from 'next/router'
import { useEffect } from 'react'
and within your component function:
const Header = () => {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url: String) => {
if (typeof window !== undefined && window.screen.width <= 991.98) { // change the width value according to your navbar breakpoint
const navbar = document.getElementById("navbar-toggler");
if (navbar !== null && !navbar.classList.contains('collapsed')) navbar.click()
}
}
router.events.on('routeChangeComplete', handleRouteChange)
}, [router.events])
return (<> ... other codes </>)
}
export default Header

Materialize Hover button doesn't work with React Router

The problem: a Materialize CSS floating button works perfectly, but when I change page (with react router dom) for example HOME > ABOUT > HOME again it doesn't anymore.
Home:
import AddBtn from "../layout/AddBtn-float";
const Home = () => {
return (
<Fragment>
<SearchBar />
<div className="container">
<AddBtn />
...
AddBtn.js
const AddBtn = () => {
return (
<div className="fixed-action-btn direction-top">
<a
href="#add-task-modal"
className="btn-floating btn-large blue darken-2 modal-trigger"
>
<i className="large material-icons">add</i>
</a>
<ul>
<li>
<a
href="#user-list-modal"
className="btn-floating green modal-trigger"
>
<i className="material-icons">person</i>
</a>
</li>
<li>
<a href="#add-user-modal" className="btn-floating red modal-trigger">
<i className="material-icons">person_add</i>
</a>
</li>
</ul>
</div>
);
};
export default AddBtn;
I'm not an expert but looks like a Materialize "bug" that needs a workaround.
Any idea?
Thanks!
I write the solution may someone needs it.
Just put in every page
useEffect(() => {
M.AutoInit();
//eslint-disable-next-line
}, []);
I used it just in App.js but looks not enough

Trying to toggle between icon to text on my navigation using React

I have some code down and it works but my problem is that it changes all of the icons at the same time when i scroll over just one. I only want the icon that I hover over to change, so any help would be appreciated.
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {isHovered: false};
this.toggleHover = this.toggleHover.bind(this);
}
toggleHover() {
this.setState(prevState => ({isHovered: !prevState.isHovered}));
}
render() {
return (
<section className="info-section">
<div className="logo">
MATT
</div>
<div className="info-box">
<ul className="nav-links">
<li onMouseEnter={this.toggleHover} onMouseLeave={this.toggleHover}>
{this.state.isHovered
? <a className="home active" href="/">Home</a>
: <a className="home active" href="/"><FontAwesomeIcon icon={faHome} /></a>
}
</li>
<li onMouseEnter={this.toggleHover} onMouseLeave={this.toggleHover}>
{this.state.isHovered
? About
: <FontAwesomeIcon icon={faUser} />
}
</li>
Maybe you can try to use CSS.
<ul className="nav-links">
<li>
<a href="/">
<span>Home</span>
<FontAwesomeIcon icon={faHome} />
</a>
</li>
<li>
<a href="/about">
<span>Home</span>
<FontAwesomeIcon icon={faUser} />
</a>
</li>
...
a {
span {
display: none;
}
&:hover {
span {
display: block;
}
/* hide the icon base on their html tag or class */
i {
display: none;
}
}
}
If you really want to handle this through Javascript (which I'm not suggesting), you can try this.
class App extends Component {
state = {
hover: ""
};
handleMouseEnter = key => {
this.setState({ hover: key });
};
handleMouseLeave = () => {
this.setState({ hover: "" });
};
render() {
const { hover } = this.state;
return (
<ul>
<li
onMouseEnter={() => this.handleMouseEnter("home")}
onMouseLeave={this.handleMouseLeave}
>
<a href="/">
{hover === "home" ? <span>Hover Home</span> : <span>Home</span>}
</a>
</li>
<li
onMouseEnter={() => this.handleMouseEnter("about")}
onMouseLeave={this.handleMouseLeave}
>
<a href="/about">
{hover === "about" ? <span>Hover about</span> : <span>about</span>}
</a>
</li>
</ul>
);
}
}

Adding a CSS class to a JSX in ReactJS

I wanted to add an 'active' class to a menu element, written in ReactJS. I tried doing it with the conventional JS method, but it failed. A click on any <li> tag, should result is removal of the 'active' class from all the <li>, and retain/ add it only to the one list tag in which the click was triggered.
Note: I know it may seem very naive on my part, but I'm just starting with ReactJS. Please ignore the stupidity.
import React, { Component } from 'react';
class Sidebar extends Component{
render(){
return(
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li className="mb-3 pl-2 menuTitle active" id="MenuTitle1">
...
</li>
<li className="mb-3 pl-2 menuTitle" id="MenuTitle2" onClick={this.clickMenu.bind(this,'MenuTitle2')}>
...
</li>
<li className="mb-3 pl-2 menuTitle" id="MenuTitle3" onClick={this.clickMenu.bind(this,'MenuTitle3')}>
...
</li>
</ul>
</div>
</div>
);
}
clickMenu(id){
// Add class 'active' on the clicked <li>, and remove from all other <li>
}
}
export default Sidebar;
I saw a similar question here, but that couldn't help me.
Idea is, store the id of clicked item in state variable and put the check with className. If item's id is same as state value then only assign the className active.
Write it like this:
class Sidebar extends Component{
constructor() {
super()
this.state = {
activeItem: 'MenuTitle1'
}
}
clickMenu(id){
// Add class 'active' on the clicked <li>, and remove from all other <li>
this.setState({
activeItem: id,
})
}
getClassName(id) {
if(id === this.state.activeItem) return 'mb-3 pl-2 menuTitle active'
return 'mb-3 pl-2 menuTitle'
}
render(){
return(
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li
id="MenuTitle1"
className={this.getClassName('MenuTitle1')}
onClick={this.clickMenu.bind(this,'MenuTitle1')}>
...
</li>
<li
id="MenuTitle2"
className={this.getClassName('MenuTitle2')}
onClick={this.clickMenu.bind(this,'MenuTitle2')}>
...
</li>
<li
id="MenuTitle3"
className={this.getClassName('MenuTitle3')}
onClick={this.clickMenu.bind(this,'MenuTitle3')}>
...
</li>
</ul>
</div>
</div>
);
}
}
You can maintain the state for clicked menu item:
clickMenu(id){
this.setState({activeMenu: id})
}
Then, define className like this:
className={
this.state.activeMenu == id {/* eg. "MenuTitle1" */}
? 'mb-3 pl-2 menuTitle active'
: 'mb-3 pl-2 menuTitle'
}
Like Bhojendra suggested store datas linked to your display inside your state then when you want to update the display of your component use the method setState, this will trigger render again (react style).
import React, { Component } from 'react';
import ReactDOM from "react-dom";
class Sidebar extends Component {
constructor() {
super();
this.state = {
activeMenuId: "MenuTitle1"
};
}
render() {
return (
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle1" ? "active" : ""}`} id="MenuTitle1" onClick={this.clickMenu.bind(this, 'MenuTitle1')}>
1
</li>
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle2" ? "active" : ""}`} id="MenuTitle2" onClick={this.clickMenu.bind(this, 'MenuTitle2')}>
2
</li>
<li className={`mb-3 pl-2 menuTitle ${this.state.activeMenuId === "MenuTitle3" ? "active" : ""}`} id="MenuTitle3" onClick={this.clickMenu.bind(this, 'MenuTitle3')}>
3
</li>
</ul>
</div>
</div>
);
}
clickMenu(id) {
// Add class 'active' on the clicked <li>, and remove from all other <li>
this.setState({activeMenuId: id});
}
}
export default Sidebar;
ReactDOM.render(<Sidebar />, document.body);
Another way of just make using initialstate and setState.
import React, { Component } from "react";
class Sidebar extends Component {
constructor(props) {
super(props);
this.initialState = {
MenuTitle1: "active",
MenuTitle2: "",
MenuTitle3: ""
};
this.state = this.initialState;
}
render() {
return (
<div className="sidebarContainer p-2">
<div className="mainMenu">
<ul className="levelOne pl-0">
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle1} `}
id="MenuTitle1"
onClick={this.clickMenu.bind(this, "MenuTitle1")}
>
one
</li>
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle2}`}
id="MenuTitle2"
onClick={this.clickMenu.bind(this, "MenuTitle2")}
>
two
</li>
<li
className={`mb-3 pl-2 menuTitle ${this.state.MenuTitle3}`}
id="MenuTitle3"
onClick={this.clickMenu.bind(this, "MenuTitle3")}
>
three
</li>
</ul>
</div>
</div>
);
}
clickMenu(id) {
this.setState(this.initialState);
this.setState({
[id]: "active"
});
}
}
export default Sidebar;

Resources