React: Reset class when click on others elements - javascript

I've created an hamburger menu that change the class name when a button is click it. This works
const Hamburger = ()=> {
const [show, setShow] = useState(false);
function showIt() {
setShow(!show);
}
return (
<HamburgerIcon className={show ? 'menu-open' : ' menu-close '} onClick={showIt}>
<span></span>
</HamburgerIcon>
)
}
export default Hamburger;
Default state for the hamburger is ".menu-close" if you click toggle to ".menu-open". But when I click in a link inside the menu, the menu remain open.
What I want to achieve is to change the class also if a link in the menu is click it.
Any suggestion on what I should do here?
Thank you!!

Add the showIt function to the link inside the menu
Code:
const Hamburger = ()=> {
const [show, setShow] = useState(false);
function showIt() {
setShow(!show);
}
return (
<HamburgerIcon className={show ? 'menu-open' : ' menu-close '} onClick={showIt}>
<a href="/some/path" onClick={showIt}>
Example
</a>
</HamburgerIcon>
)
}
export default Hamburger;

You can lift the state up to the parent component.
In the example below, I've lifted the state up to the lowest common ancestor component of Menu and Hamburger components. And then passed down the state via props.
function Hamburger({ isOpen, onClick }) {
return <button onClick={onClick}>Menu {isOpen ? "↓" : "→"}</button>;
}
function Menu({ isOpen, menuItems, onClick }) {
if (!isOpen) {
return null;
}
return (
<ul>
{menuItems.map((item, index) => (
<li key={index}>
<button onClick={onClick}>{item}</button>
</li>
))}
</ul>
);
}
function App() {
const [isOpen, setIsOpen] = React.useState(false);
const handleClick = () => setIsOpen(!isOpen);
return (
<nav>
<Hamburger isOpen={isOpen} onClick={handleClick} />
<Menu
isOpen={isOpen}
onClick={handleClick}
menuItems={["Home", "About", "Contact"]}
/>
</nav>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
ul {
list-style: none;
}
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>

Related

ClickAwayListener not working with Collapse or Fade transitions

I'm trying to create a notifications area. I show a notification icon, and when the user clicks on it, I show the list of notifications.
Here's a codesandbox
The problem is that I can't mix it with ClickAwayListener.
When I use ClickAwayListener it's not shown at all.
How should I fix this?
HeaderAction.js
import Tooltip from "#material-ui/core/Tooltip";
import Fade from "#material-ui/core/Fade";
import Collapse from "#material-ui/core/Collapse";
import React, { useState } from "react";
import ClickAwayListener from "#material-ui/core/ClickAwayListener";
import Icon from "#material-ui/core/Icon";
const HeaderAction = ({ icon, title, component }) => {
const Component = component || (() => <div>NA</div>);
const [showComponent, setShowComponent] = useState(false);
const handleClick = () => {
setShowComponent(!showComponent);
};
return (
<>
<Tooltip title={title || ""}>
<div onClick={() => handleClick()}>
<Icon>{icon}</Icon>
</div>
</Tooltip>
{/* This part is not working */}
{/* <ClickAwayListener onClickAway={() => setShowComponent(false)}>
<div>
<Fade in={showComponent}>
<div>
<Component />
</div>
</Fade>
</div>
</ClickAwayListener> */}
<Fade in={showComponent}>
<div>
<Component />
</div>
</Fade>
</>
);
};
export { HeaderAction };
When you click the icon button, handleClick is called and the showComponent state is set to true, but then onClickAway from ClickAwayListener is also called and set the showComponent state to false again. The fix is simple, don't let the onClickAway handler execute by stopping the propagation after clicking the button:
<div
onClick={(e) => {
e.stopPropagation();
handleClick();
}}
>

onClick detection for both an li and button nested within li

so i'm trying to implement the line-through feature while having a delete button. clicking on the li text crosses out the item, and clicking on the del button removes it.
functionally, it works. the issue is when I delete an itme, say "2", it will apply the line-through style to the list item below it. i'm guessing this is because "onClick" is detected twice - both inside the list item and the button (because the button is technically nested within the list item). the moment I press on the DEL button for 2, the onClick is detected for list item 3, applying the line-through style. what would be the best way to go about correcting this?
my code with an App component and ListItem component:
import React, { useState } from "react";
import ListItem from "./ListItem";
function App() {
const [inputText, setInputText] = useState("");
const [items, setItems] = useState([]);
function handleChange(event) {
const newValue = event.target.value;
setInputText(newValue);
}
function addItem() {
setItems((prevItems) => {
return [...prevItems, inputText];
});
setInputText("");
}
function deleteItem(id) {
setItems((prevItems) => {
return prevItems.filter((item, index) => {
return index !== id;
});
});
}
return (
<div className="container">
<div className="heading">
<h1>To-Do List</h1>
</div>
<div className="form">
<input onChange={handleChange} type="text" value={inputText} />
<button onClick={addItem}>
<span>Add</span>
</button>
</div>
<div>
<ul>
{items.map((todoItem, index) => (
<ListItem
key={index}
id={index}
item={todoItem}
delete={deleteItem}
/>
))}
</ul>
</div>
</div>
);
}
export default App;
____________________________________________________________________________________________________
import React, { useState } from "react";
function ListItem(props) {
const [clickedOn, setClickedOn] = useState(false);
function handleClick() {
setClickedOn((prevValue) => {
return !prevValue;
});
}
return (
<div>
<li
onClick={handleClick}
style={{ textDecoration: clickedOn ? "line-through" : "none" }}
>
{props.item}
<button
onClick={() => {
props.delete(props.id);
}}
style={{ float: "right" }}
>
<span>Del</span>
</button>
</li>
</div>
);
}
export default ListItem;
As you already wrote, user events are propagated up the DOM tree. To stop the propagation, you can use event.stopPropagation() ref in your event handler
<button
onClick={(event) => {
event.stopPropagation();
props.delete(props.id);
}}
style={{ float: "right" }}
>

React.js how to update reset my navbar when changing url?

I have created a navbar with items but I cannot find a way to reset the navbar when changing of URL.
NAV-item.jsx (this is where I can click on the button to make my navbar appear or disappear)
function NavItem (props) {
const [open,setOpen] = useState(false);
return(
<li className='nav-item'>
<a
className='icon-button'
onClick= {() => setOpen(!open)}>
{props.icon}
</a>
{open && props.children}
</li>
)
}
Directory.jsx (where my links are I tried to do history.push but I received an undefined error)
class Directory extends React.Component {
constructor(){
super();
this.state = {
Page1: PagesPrimaire,
Page2: PagesSecondaire
};
}
render() {
return(
<div className='menu-item'>
{
this.state.Page1.map(({id,title,linkUrl,history,match}) =>(
<Link key={id}
className='menu-item'
//to={`${linkUrl}`}
onClick = {() => history.push(this.state.Page1.linkUrl)}
>{title}</Link>
)
)
}
</div>
)
}
}
export default withRouter(Directory);
DropdownMenu.jsx (my navbar and some CSStransition tricks)
function DropdownMenu () {
const [activeMenu, setActiveMenu] = useState('main');
const DropdownItem = (props) =>
(
<a
className='menu-item'
onClick={()=>props.goToMenu && setActiveMenu(props.goToMenu)}>
{props.children}
</a>
)
return (
<div className='dropdown'>
<CSSTransition
in={activeMenu === 'main' }
unmountOnExit
timeout={500}
classNames='menu-primary'
>
<div className='menu'>
<Directory/>
<DropdownItem goToMenu='connexion'>Devenir Membre/connexion</DropdownItem>
</div>
</CSSTransition>
<CSSTransition
in={activeMenu === 'connexion' }
unmountOnExit
timeout={500}
classNames='menu-secondary'
>
<div className='menu'>
<DropdownItem goToMenu='main'><img src={Leftarrow} className='arrow' alt=''></img></DropdownItem>
</div>
</CSSTransition>
</div>
)
}
export default DropdownMenu;
Right now I can make the navbar appear and disappear by clicking a button but I cannot make it disappear when I change of Url.
Thank a lot,
as #kritiz enter a navabar for each of the route and navigate to the one you need or use the same one and use props to decide what to do there is not other way as far as I know.
the only two version I know is:
1.outside of switch
2.inside of each component with props to decied what to do in the nav

My modal doesn't show when I click a button

Here I have my modal component. I am making an app that I want a button to open this modal that I use in multiple places like opening a preview or deleting options.
import React from 'react';
import ReactDOM from 'react-dom';
import { CSSTransition } from 'react-transition-group';
import Backdrop from '../Backdrop/Backdrop';
import '../Modal/Modal.css';
const ModalOverlay = (props) => {
const content = (
<div className={`modal ${props.className}`} style={props.style}>
<header className={`modal__header ${props.headerClass}`}>
<h2>{props.header}</h2>
</header>
<form
onSubmit={
props.onSubmit ? props.onSubmit : (event) => event.preventDefault()
}
>
<div className={`modal__content ${props.contentClass}`}>
{props.children}
</div>
<footer className={`modal__footer ${props.footerClass}`}>
{props.footer}
</footer>
</form>
</div>
);
return ReactDOM.createPortal(content, document.getElementById('modal-hook'));
};
const Modal = (props) => {
return (
<React.Fragment>
{props.show && <Backdrop onClick={props.onCancel} />}
<CSSTransition
in={props.show}
mountOnEnter
unmountOnExit
timeout={200}
classNames="modal"
>
<ModalOverlay {...props} />
</CSSTransition>
</React.Fragment>
);
};
export default Modal;
And here I use this modal for showing up deleting options.
const DocumentItem = (props) => {
const [showConfirmModal, setShowConfirmModal] = useState(false);
const showDeleteWarningHandler = () => {
setShowConfirmModal(true);
};
const calcelDeleteHandler = () => {
setShowConfirmModal(false);
};
const confirmDeleteHandler = () => {
setShowConfirmModal(false);
console.log('Delete!');
};
return (
<React.Fragment>
<Modal
show={showConfirmModal}
onCancel={calcelDeleteHandler}
header="Are you sure?"
footerClass="document-item__modal-actions"
footer={
<React.Fragment>
<Button inverse onClick={calcelDeleteHandler}>
CANCEL
</Button>
<Button danger onClick={confirmDeleteHandler}>
DELETE
</Button>
</React.Fragment>
}
>
<p>
Do you want to proceed and delete this document? Please note that it
can't be undone thereafter.
</p>
</Modal>
</React.Fragment>
);
};
I don't understand why my screen goes all black, transparent but my modal doesn't show.
How can I fix this problem?

How to pass prop to component's children

const ListView = () => {
return(
<ul>
<ListItem modal={<Modal />} />
</ul>
)
};
const ListItem = (props) => {
const [visible, setVisible] = useState(false);
const toggle = () => setVisible(!visible)
return (
<>
<li>
ListItem
</li>
<ModalWrapper toggle={toggle}>{props.modal}</ModalWrapper>
</>
)
}
const ModalWrapper = (props) => {
if(!props.visible) return null;
return (
<>
{props.children}
</>
)
}
const Modal = ({ toggle }) => {
/* I would like to use toggle() here. */
return (
<>
<div onClick={toggle} className="dimmer"></div>
<div className="modal">modal</div>
</>
)
}
I have a function toggle() in <ListItem /> as shown above.
I am struggling to use toggle() in <Modal />.
Is it possible or are there any suggestions?
You need to inject toggle to ModalWrapper children, be careful not to override toggle prop on Modal after it.
const ModalWrapper = ({ children, visible, toggle }) => {
const injected = React.Children.map(children, child =>
React.cloneElement(child, { toggle })
);
return <>{visible && injected}</>;
};
Refer to React.cloneElement and React.Children.map.
Demo:

Categories

Resources