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

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/

Related

React - toggleClass on button activate all buttons

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);}}

React menu toggle typeError (gsap, object is not extensible)

I having some hard time to figure out some issues on my Gatsby application (i'm new to Gatssby. Basically I'm trying to implement open, close menu functionality using GSAP. The issue I'm facing is in my if else conditional statement then I'm toggling between the open close menu states.
Strange thing is on my if statement gsap works just fine, but on else condition, then I add gsap function in it I get error "can't define property "_gsap": Object is not extensible". Because I'm new to React and GSAP, I can't figure out what I'm doing wrong? There from this error coming from and how could I solve it?
Here is my code:
menu.js
import React, { useEffect, useRef } from "react";
import { Link } from "gatsby";
import Navbar from "./Navbar";
import gsap from "gsap";
import {
Github,
LinkedIn,
Instagram,
Facebook,
Twitter,
Close,
} from "../assets/svg/social-icons";
const Menu = ({ menuState, setMenuState }) => {
// Create varibles of our dom nodes
let menu = useRef(null);
let revealMenu = useRef(null);
let revealMenuBackground = useRef(null);
useEffect(() => {
if (menuState) {
console.log("CLOSE");
gsap.to([revealMenu, revealMenuBackground], {
duration: 0.8,
height: 0,
ease: "power3.inOut",
stagger: { amount: 0.07 },
});
gsap.to(menu, {
duration: 1,
css: { display: "none" },
});
} else if (!menuState) {
console.log("OPEN");
// ERROR COMES FROM HERE
gsap.to([revealMenu, revealMenuBackground], {});
}
});
return (
<>
{menuState && (
<div ref={(el) => (menu = el)} className="hamburger-menu">
<div
ref={(el) => (revealMenuBackground = el)}
className="menu-secondary-background-color"
></div>
<div ref={(el) => (revealMenu = el)} className="menu-layer">
<div className="wrapper">
<div className="container">
<div onClick={() => setMenuState(!menuState)} className="close">
<Close />
</div>
</div>
<div className="menu-city-background"></div>
<span className="menu-text">Menu</span>
<div className="menu-links">
<Navbar />
</div>
<div className="menu-social-icons">
<IconList />
</div>
<button className="btn menu-resume">Resume</button>
</div>
</div>
</div>
)}
</>
);
};
const IconList = () => {
return (
<li>
<a className="icon social-icon" href="#">
<Github />
</a>
<a className="icon social-icon" href="#">
<Instagram />
</a>
<a className="icon social-icon" href="#">
<LinkedIn />
</a>
<a className="icon social-icon" href="#">
<Facebook />
</a>
<a className="icon social-icon" href="#">
<Twitter />
</a>
</li>
);
};
export default Menu;
When using useRef, you can access your element (what gsap needs) with reference.current.
In your case, you should add .current to menu, revealMenu and revealMenuBackground, otherwise gsap won't understand what you are passing.
Example:
gsap.to([revealMenu.current, revealMenuBackground.current], {});
Also, on your return you should define your references as for example ref={menu} so the reference of that element gets attached to the current reference defined by useRef.
Last but not least, in the useEffect you should wrap everything within a condition to check if that ref contains a value, as such:
useEffect(() => {
if(menu?.current) {
// Do your animations here
}
}
This is because of React components lifecycle, where:
The reference gets instanciated with null
useEffect runs and the ref will be null
The component renders the jsx (this is where the ref gets attached to its element)
Now useEffect will contain the references

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

Close menu after selecting an anchor link React and Gatsby

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)
}

ReactJS Hamburger icon not toggling

I am using this library for front end which is based on Bulma and I'm facing issues with Hamburger Icon Here is the documentation Example, but again this is something not very easy to understand. I have searched for a workaround and a solution for this, but I cannot find it, I'm doing it in ES6 Style, and here is my code.
import React, { Component } from "react";
import { Navbar } from "react-bulma-components/full";
class MenuNavbar extends Component {
render() {
return (
<div id="header">
<Navbar color="info" fixed="top">
<Navbar.Brand>
<Navbar.Item renderAs="a" href="/">
<img src="https://i.imgur.com/9jQaBuq.png" alt="Dew Ventures" />
</Navbar.Item>
<Navbar.Burger />
</Navbar.Brand>
<Navbar.Menu>
<Navbar.Container>
<Navbar.Item href="/">Home</Navbar.Item>
<Navbar.Item href="/about"> About Us</Navbar.Item>
<Navbar.Item href="/contact"> Contact Us</Navbar.Item>
</Navbar.Container>
</Navbar.Menu>
</Navbar>
</div>
);
}
}
export default MenuNavbar;
<Navbar.Burger
active={open}
onClick={() =>
this.setState(state => {
open: !state.open;
})
}
/>
From the Storybook you linked to, the example shows that there is an onClick handler that sets the state to change the hamburger into a cross. You need to have some kind of handler that sets the active prop to true. That will change the hamburger to a cross whenever you click the component.
And from the source code of that library for the burger component within the Navbar that you're using, the component requires you to pass in the active prop as true to set the is-active css class, which Bulma uses natively to change the hamburger to a cross:
import React, { Component } from "react";
import { Navbar } from "react-bulma-components/full";
class MenuNavbar extends Component {
// set active state for hamburger
state = { active : false }
handleClick = () => {
const { active } = this.state;
this.setState({ active: !active });
}
render() {
return (
<div id="header">
<Navbar color="info" fixed="top" active={this.state.active}>
<Navbar.Brand>
<Navbar.Item renderAs="a" href="/">
<img src="https://i.imgur.com/9jQaBuq.png" alt="Dew Ventures" />
</Navbar.Item>
<Navbar.Burger
active={this.state.active}
onClick={this.handleClick}
/>
</Navbar.Brand>
<Navbar.Menu>
<Navbar.Container>
<Navbar.Item href="/">Home</Navbar.Item>
<Navbar.Item href="/about"> About Us</Navbar.Item>
<Navbar.Item href="/contact"> Contact Us</Navbar.Item>
</Navbar.Container>
</Navbar.Menu>
</Navbar>
</div>
);
}
}
export default MenuNavbar;
You can toggle navbar in pure Bulma CSS with ref usage:
burger = React.createRef();
menu = React.createRef();
toggle = () => {
if (this.menu.current.classList.contains("is-active")) {
this.menu.current.classList.remove("is-active");
} else {
this.menu.current.classList.add("is-active");
}
};
this approach adopted also for your case
Plug and Play React usag utilizing UseEffect...
Remember to add the nav-toggle and navbar-menu classes to your burger and nav respectively
useEffect(() => {
(function() {
var burger = document.querySelector('.nav-toggle');
var menu = document.querySelector('.navbar-menu');
burger.addEventListener('click', function() {
burger.classList.toggle('is-active');
menu.classList.toggle('is-active');
});
})();
}, [])
<nav className="navbar">
<div className="navbar-brand is-1">
<a href="/" className="navbar-item">
<img src={logo} alt="YMCA"/>
</a>
<button className="navbar-burger burger is-white button nav-toggle"
aria-label="menu" aria-expanded="false" data-target="Options">
<span></span>
<span></span>
<span></span>
<span></span>
</button>
</div>
<div className="navbar-menu is-8 is-offset-a" id="Options">
<div className="navbar-end mt-2">
<button type="submit">Association Level Prediction</button>
<button type="submit" >Custom Prediction</button>
<button type="submit" onClick={() => handleNavigate(ROUTE_ABOUT)} className={checkIfActive(ROUTE_ABOUT)}>About</button>
<button type="submit" onClick={() => handleLogout()} className={checkIfActive('logout')}>Logout</button>
</div>
</div>
</nav>

Resources