Materialize Hover button doesn't work with React Router - reactjs

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

Related

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

react-router-dom Link does not navigate to the start of target pages

I'm using react-router-dom Link on Home page to navigate to some routes. It works correctly when I'm at the top of Home Page. But when I scroll down the Home and click the Link items in navbar, although it navigates to the specified route, the target page is not displayed from the top. It starts somewhere in the middle of the page.
Here's my navbar:
import React, { Fragment, useState } from "react";
import { Link } from "react-router-dom";
import Fade from "react-reveal/Fade";
import EduLogo from "./resource/edu.png";
import Logo from "./resource/logo.png";
const Navbar = () => {
const [nav, setNav] = useState(false);
const adjustNav = () => {
if (window.scrollY >= 1) {
setNav(true);
document.getElementById("main-nav").style.height = "100px";
document.getElementById("nav-logo").style.height = "30px";
document.getElementById("nav-logo").style.width = "275px";
document.getElementById("nav-edu").style.height = "65px";
document.getElementById("nav-edu").style.width = "200px";
} else {
setNav(false);
document.getElementById("main-nav").style.height = "120px";
document.getElementById("nav-logo").style.height = "37px";
document.getElementById("nav-logo").style.width = "360px";
document.getElementById("nav-edu").style.height = "80px";
document.getElementById("nav-edu").style.width = "238px";
}
};
window.addEventListener("scroll", adjustNav);
return (
<Fragment>
<nav id="main-nav" className={nav ? "main-nav active" : "main-nav"}>
<Fade left>
<div id="main-nav-logo">
<a href="#!" target="_blank">
<img src={EduLogo} alt="" id="nav-edu" />
</a>
<img
src={Logo}
alt=""
id="nav-logo"
/>
</div>
</Fade>
<div className="nav-container">
<Fade bottom cascade>
<ul>
<li id="nav-item">
<Link to="/" className="nav-item">
Home
</Link>
</li>
<li id="nav-item">
<Link to="/members" className="nav-item">
Who We Are?
</Link>
</li>
<li id="nav-item">
<Link to="/research" className="nav-item">
What We Do?
</Link>
</li>
<li id="nav-item">
<Link to="/latest" className="nav-item">
News & Events
</Link>
</li>
</ul>
</Fade>
</div>
</nav>
</Fragment>
);
};
export default Navbar;
Any suggestion?
Thanks
I normally use something like this to make sure that my page is shown from the top whenever it is visited.
React.useLayoutEffect(() => {
window.scrollTo(0, 0);
}, [])
I use the useLayoutEffect hook to apply the window scroll before the browser starts painting.
You can read more about it here

How to get side drawer to close when clicking on link

I have a navbar component that opens a side drawer when clicking the burger menu on a mobile device. But when I click a link it goes to the 'page' but the side drawer does not close.
Do I have to add a click event to every link to change the toggle state to false? That seems like the wrong, long winded approach.
const NavBar = (props) => {
const [toggle, setToggled] = useState(false);
const toggleTrueFalse = () => setToggled(!toggle);
return (
<nav>
<div className="logo">
<h4>The nav</h4>
</div>
<ul className={toggle ? "nav-links nav-active" : "nav-links"}>
<li>
<Link to="/" activeclassname="active">
Home
</Link>
</li>
<li>
<Link to="/faq" activeclassname="active">
FAQ
</Link>
</li>
<li>
<Link to="/contact" activeclassname="active">
Contact us
</Link>
</li>
</ul>
<div
className={toggle ? "burger linetoggle" : "burger"}
onClick={toggleTrueFalse}
>
<div className="line1"></div>
<div className="line2"></div>
<div className="line3"></div>
</div>
</nav>
);
};
export default NavBar;
you can have link array and map it :
const links = []
\\in render
links.map((link,index)=>{
return(<li key=index onclick={toggleTrueFalse}>
<Link to={link.to} activeclassname="active">
{link.txt}
</Link>
</li>)
}

How to change the active class in a navbar in react

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.

Could the Delete modal be getting hijacked by React-Router?

I'm trying to move the delete button's functionality into a modal, so the user should get a confirmation before deleting from a single click. If I open the inspector I can manually make it appear by changing the display:None in CSS, but I thought that's what the Materialize library was handling for me.
When I click on the modal, I see it appearing in the address bar, so I assume react-router is hijacking the modal
I can probably replace the exact path to match /modal2, but should I be sending it to a new component? Or send it back to the same component with a property set for modal?
```
!jsx
import React from 'react';
import { Switch, Route} from 'react-router-dom';
import Barrels from './Barrels';
import About from './About';
import BarrelDetails from './BarrelDetails';
import AddBarrel from './AddBarrel';
import BarrelEdit from './BarrelEdit';
const Main = () => (
<main className="green">
<Switch>
<Route exact path= '/' component={Barrels} />
<Route exact path= '/about' component={About} />
<Route exact path= '/barrels/add' component={AddBarrel} />
<Route exact path= '/barrels/edit/:id' component={BarrelEdit} />
<Route exact path= '#modal2' component={BarrelEdit //modal component propperty turned on?// } />
<Route exact path= '/barrels/:id' component={BarrelDetails} />
</Switch>
</main>
)
export default Main;
```
live demo on Heroku
Repository on BitBucket
or should I be trying to move the modal trigger into the on onDelete function?
```
!jsx
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import logo from '../logo.svg';
class BarrelDetails extends Component {
constructor (props){
super(props);
this.state = {
details: ''
}
}
componentWillMount(){
this.getBarrel();
}
getBarrel(){
let barrelID = this.props.match.params.id;
axios.get(`/api/Barrels/${barrelID}`)
.then (response => {
this.setState({details: response.data}, () =>
{
console.log(this.state);
})
})
.catch(err => console.log(err));
}
onDelete(){
let barrelID = this.state.details.id;
axios.delete(`/api/Barrels/${barrelID}`)
.then ( response => {
this.props.history.push('/');
} ).catch(err => console.log(err));
}
render () {
return (
<div className = "container" >
<header className="App-header z-depth-3">
<h2>{this.state.details.Name}</h2>
<Link className = "btn grey" to = "/">back</Link>
</header>
<ul className = "collection z-depth-3" >
<li className = "collection-item" >planted: <b className = "yellow" > {this.state.details.date_planted}</b> </li>
<li className = "collection-item" >Barrel #: <b className = "yellow" > {this.state.details.barrel_number}</b> </li>
<li className = "collection-item" ><b className = "yellow" > {this.state.details.contents}</b> </li>
<li className = "collection-item" >location: <b className = "yellow" > {this.state.details.location}</b> </li>
<li className = "collection-item" >geolocation: <b className = "yellow" > this.state.details.geoLocaction.toString()</b> </li>
<li className = "collection-item" >notes: <b className = "yellow" > {this.state.details.notes}</b> </li>
<li className = "collection-item" >size: <b className = "yellow" > {this.state.details.size}</b> </li>
<li className = "collection-item" >last checked: <b className = "yellow" > {this.state.details.date_last_checked}</b> </li>
</ul>
<button onClick = {this.onDelete.bind(this) } className = "btn red right"><i className ="far fa-trash-alt"></i> Delete this Barrel</button>
<h5>what that modal do?</h5>
<Link to={`/barrels/edit/${this.state.details.id}`} className="btn waves-effect z-depth-3"><i className = "fas fa-pencil-alt" ></i> Edit this Barrel</Link>
<Link to={`#modal2`} className="btn waves-effect red"><i className ="far fa-trash-alt z-depth-3"></i> Delete this Barrel</Link>
<div id="modal1" className="modal">
<div className="modal-content">
<h4>Modal Header</h4>
<p>A bunch of text</p>
</div>
<div className="modal-footer">
Button
</div>
</div>
<div id="modal2" className="modal orange">
<div className="modal-content">
<h4>Are you sure you want to delete</h4>
<p>A bunch of text</p>
</div>
<div className="modal-footer">
Button
</div>
</div>
<p className="App-intro">
TurtleWolfe.com<br/>
using LoopBack & React<br/>
<img src={logo} className="App-logo" alt="logo" />
</p>
</div>
)
}
}
export default BarrelDetails;
```
<Link to={`#modal2`} className="btn waves-effect red">
<i className ="far fa-trash-alt z-depth-3"></i>
Delete this Barrel
</Link>
This is the part that concerns me. This should not be a <Link> as that is attached to React-Router, you should just be using a button with the styling turned off, then triggering the modal via an onClick event.
You're also trying to show your modal in a way that probably won't work with React. You're going to want to set a local state of dislpayModal: false or something like that, then do a check for that state in your render, instead of relying on Materalize to do it for you. It can be tricky to get DOM based plugins to work in a React environment but doing it with the state is the "React Way" of doing things like this.
Another suggestion for common CSS frameworks with JavaScript pieces built on the DOM is to justo find a 3rd party React based implementation that has already done this for you. Such as: https://react-materialize.github.io/

Resources